英文:
instantiation of static std::map inside a c++ template class
问题
我遇到了一些让C++模板正常工作的问题。以下是我想要做的事情:
template <class T, const int I = 16, const int N = 65536, const int M = 2>
class pool
{
public:
pool();
~pool();
void clear();
T * get();
void put(void * p);
};
template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
static std::map<pthread_t, pool<T,I,N,M>* > pools;
public:
typedef typename std::map<pthread_t, pool<T,I,N,M>* >::iterator iterator_type;
static void registerThread(pthread_t id) {
if (pools.find(id) == pools.end())
pools[id] = new pool<T,I,N,M>();
}
static void registerThread() {
registerThread(pthread_self());
}
static void clear() {
for (iterator_type it = pools.begin(); it != pools.end(); ++it)
it->second->clear();
}
static pool<T,I,N,M>* getPool() {
if (pools.find(pthread_self()) == pools.end()) // <-- warning here
registerThread();
return pools[pthread_self()];
}
};
这个想法是有一个池的池,其中当前线程总是从它自己的池中分配,但任何线程都可以释放对象,并且它们将返回到正确的池中。对象自身内部存储了它们的原始池的指针。当运行时,工作线程将分配对象,但单个收集器线程在完成时会释放它们。
请注意static std::map
。当我尝试将其实例化为:
template<typename T, int I, int N, int M> std::map<pthread_t, pool<T,I,N,M>* > pool_of_pools<T,I,N,M>::pools;
这会导致警告:要求实例化变量'pool_of_pools<int>::pools',但没有可用的定义
。它编译并运行,但当不同的线程调用getPool
时,它们似乎会注册到自己线程本地的pools
版本。我创建一个线程,然后从主线程注册两个线程,但当我从创建的线程调用getPool
时,pools
的地址(&pools
)与我从主线程注册两个线程时的地址不同。我不确定如何可能发生这种情况,因为它是静态的和全局的。
当我尝试将映射实例化为:
template<> std::map<pthread_t, void*> pool_of_pools<int,512*1024,1024*1024,2>::pools;
它无法编译。我得到一个链接器错误undefined reference to pool_of_pools<int, 52488, 1048576, 2>::pools
。
我以前做过单一模板类,但从未在其中有一个静态的模板类实例。我该如何做?这个问题是否可能解决?
英文:
I'm having trouble getting some c++ templates to work. Here's what I want to do:
template <class T, const int I = 16, const int N = 65536, const int M = 2>
class pool
{
public:
pool();
~pool();
void clear();
T * get();
void put(void * p);
};
template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
static std::map<pthread_t, pool<T,I,N,M>*> pools;
public:
typedef typename std::map<pthread_t, pool<T,I,N,M>*>::iterator iterator_type;
static void registerThread(pthread_t id) {
if (pools.find(id) == pools.end())
pools[id] = new pool<T,I,N,M>();
}
static void registerThread() {
registerThread(pthread_self());
}
static void clear() {
for (iterator_type it = pools.begin(); it != pools.end(); ++it)
it->second->clear();
}
static pool<T,I,N,M>* getPool() {
if (pools.find(pthread_self()) == pools.end()) // <-- warning here
registerThread();
return pools[pthread_self()];
}
};
The idea is to have a pool of pools where the current thread always allocates from it's own pool
but any thread can release the objects and they will go back to the correct pool. A pointer to their origin pool is stored within the object itself. When it runs, worker threads will allocate objects but a single collector thread will free them all when done.
Notice the static std::map
. When I try to instantiate that as:
template<typename T, int I, int N, int M> std::map<pthread_t, pool<T,I,N,M>*> pool_of_pools<T,I,N,M>::pools;
this gives me a warning of instantiation of variable 'pool_of_pools<int>::pools' required here, but no definition is available
. It compiles and runs, but when different threads call getPool
, they seem to register with their own thread-local(?) versions of pools
. I create a thread, then call registerThread(id)
with the thread id, and also registerThread()
to register the main thread. But when I call getPool
from the created thread, the address of pools
(&pools
) is different than what it was when I registered the two threads from the main thread. I'm not sure how that's possible since it's static and a global.
When I try to instantiate the map as:
template<> std::map<pthread_t, void*> pool_of_pools<int,512*1024,1024*1024,2>::pools;
it won't compile. I get a linker error undefined reference to pool_of_pools<int, 52488, 1048576, 2>::pools
I've done single templated classes before but never a templated class that has a static templated class instance in it.
How do I do this? Is it even possible?
答案1
得分: 3
以下是您要翻译的内容:
"static needs to be defined somewhere for any instantiation of the class template. A way around this is to encapsulate your static data in a function. This does mean that it's not constructed until something uses it, but that probably isn't a big issue for you.
So, instead of using pools
everywhere, turn that into the function pools()
which owns the static. Provided you're using at least C++11, the standard guarantees thread-safety which protects initialization of the static data.
static std::map<pthread_t, pool<T,I,N,M>*>& pools()
{
static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
return s_pools;
}
Then it's just a matter of changing everywhere that you use the identifier pools
to instead call the function pools()
.
Full code with these changes:
template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
static std::map<pthread_t, pool<T,I,N,M>*>& pools()
{
static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
return s_pools;
}
public:
typedef typename std::map<pthread_t, pool<T,I,N,M>*>::iterator iterator_type;
static void registerThread(pthread_t id) {
if (pools().find(id) == pools().end())
pools()[id] = new pool<T,I,N,M>();
}
static void registerThread() {
registerThread(pthread_self());
}
static void clear() {
for (iterator_type it = pools().begin(); it != pools().end(); ++it)
it->second->clear();
}
static pool<T,I,N,M>* getPool() {
if (pools().find(pthread_self()) == pools().end()) // <-- warning here
registerThread();
return pools()[pthread_self()];
}
};
英文:
The static needs to be defined somewhere for any instantiation of the class template. A way around this is to encapsulate your static data in a function. This does mean that it's not constructed until something uses it, but that probably isn't a big issue for you.
So, instead of using pools
everywhere, turn that into the function pools()
which owns the static. Provided you're using at least C++11, the standard guarantees thread-safety which protects initialization of the static data.
static std::map<pthread_t, pool<T,I,N,M>*>& pools()
{
static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
return s_pools;
}
Then it's just a matter of changing everywhere that you use the identifier pools
to instead call the function pools()
.
Full code with these changes:
template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
static std::map<pthread_t, pool<T,I,N,M>*>& pools()
{
static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
return s_pools;
}
public:
typedef typename std::map<pthread_t, pool<T,I,N,M>*>::iterator iterator_type;
static void registerThread(pthread_t id) {
if (pools().find(id) == pools().end())
pools()[id] = new pool<T,I,N,M>();
}
static void registerThread() {
registerThread(pthread_self());
}
static void clear() {
for (iterator_type it = pools().begin(); it != pools().end(); ++it)
it->second->clear();
}
static pool<T,I,N,M>* getPool() {
if (pools().find(pthread_self()) == pools().end()) // <-- warning here
registerThread();
return pools()[pthread_self()];
}
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论