是否可以在类中创建非静态模板字段?
如果没有,如何解决?
应根据需要在编译时创建此类字段。
我有很多B
- 类,例如B1
,B2
,B3
。
(在实际情况中,他们有更有意义的名字。)
我想要创建一个具有非静态模板函数D
的类add<BX>()
,每次调用它时,每个人都需要counter++
BX
,用于D.的某个实例
(在实际情况下,它会使某些事情变得更复杂。)
这是一个实现它的工作demo
遗憾的是,我目前必须在BX
内对每B1
个,B2
,B3
,D
进行硬编码: -
class B1{};class B2{};class B3{};
class Counter{
public: int counter=0;
};
template<class BX>class Tag{};
class D{
Counter countB1;
Counter countB2;
Counter countB3;
public: template<class BX> void add(){
add_(Tag<BX>());
}
private:
void add_(Tag<B1>){ countB1.counter++;}
void add_(Tag<B2>){ countB2.counter++;}
void add_(Tag<B3>){ countB3.counter++;}
public: template<class BX> int get(){
return get_(Tag<BX>());
}
private:
int get_(Tag<B1>){ return countB1.counter;}
int get_(Tag<B2>){ return countB2.counter;}
int get_(Tag<B3>){ return countB3.counter;}
};
以下是用法。请注意,D
的每个实例都保留自己的counter
: -
int main() {
D d1;
d1.add<B2>(); d1.add<B2>(); d1.add<B3>();
std::cout<<d1.get<B1>()<<" "<<d1.get<B2>()<<" "<<d1.get<B3>()<<"\n";
//^ print 0 2 1
D d2;
d2.add<B1>();
std::cout<<d2.get<B1>()<<" "<<d2.get<B2>()<<" "<<d2.get<B3>()<<"\n";
//^ print 1 0 0 (not 1 2 1)
return 0;
}
我的梦想是: -
class D{
Counter<BX> countBX; //???
public: template<class BX> void add(){
Counter<BX>::getNonStaticInstance(this).counter++; //???
}
public: template<class BX> int get(){
return Counter<BX>::getNonStaticInstance(this).counter; //???
}
};
如果countBX
是静态的,我知道怎么做,但对于非静态,它似乎是不可能的。
答案 0 :(得分:10)
您不需要RTTI
来解决这个问题,也不需要std::map
来解决这个问题(特别是RTTI)。变量模板和继承可以为您解决此问题:
class B1 {}; class B2 {}; class B3 {};
template<typename T>
class Counter {
public:
int counter = 0;
};
template<class... BXs>
class D : public Counter<BXs>... {
public:
template<typename B>
void add() {
Counter<B>::counter++;
}
template<typename B>
int get() {
return Counter<B>::counter;
}
};
这与你真正想要的非常接近(顺便说一句,你是在正确的轨道上)。
答案 1 :(得分:4)
使用 std::map
std::unordered_map
(来自Yakk的建议;感谢)索引和RTTI?
#include <map>
#include <iostream>
#include <typeindex>
class B1 {};
class B2 {};
class B3 {};
class D
{
private:
std::unordered_map<std::type_index, std::size_t> bxMap;
public:
template <typename BX>
void add ()
{ ++ bxMap[std::type_index(typeid(BX))]; }
template <typename BX>
int get ()
{ return bxMap[std::type_index(typeid(BX))]; }
};
int main ()
{
D d1;
d1.add<B2>(); d1.add<B2>(); d1.add<B3>();
std::cout<<d1.get<B1>()<<" "<<d1.get<B2>()<<" "<<d1.get<B3>()<<"\n";
//^ print 0 2 1
D d2;
d2.add<B1>();
std::cout<<d2.get<B1>()<<" "<<d2.get<B2>()<<" "<<d2.get<B3>()<<"\n";
//^ print 1 0 0
return 0;
}
答案 2 :(得分:4)
不幸的是,在我们对标准进行反思之前,没有简单的方法来迭代一个类的成员。
直到那时的解决方案要么自己实现反射(很难并经常使用带有自己的问题的宏,如可调试性),要么将类型包装成另一种类型(更容易)。
我们可以使用具有std::array
个计数器的基类来执行此操作,每个BX
一个:
template<class... Bs>
struct Base
{
std::array<Counter, sizeof...(Bs)> counters;
// ... more on this later
};
然后我们的D
类可以从它派生并得到它需要的计数器:
struct D : Base<B1, B2, B3>{ /*...*/};
接下来我们要做的是在基类中实现一个IndexOf
函数,它允许我们转换一个类型(B1
B2
B3
之一进入指数。
我们可以使用类型特征和折叠表达式来执行此操作:
template<class T>
static constexpr int IndexOf()
{
// find index of T in Bs...
int toReturn = 0;
int index = 0;
(..., (std::is_same_v<T, Bs> ? toReturn = index : ++index));
return toReturn;
}
现在我们的D
类非常简化,并且不依赖于标记调度:
struct D : Base<B1, B2, B3>{
template<class BX>
void add(){
counters[IndexOf<BX>()].counter++;
}
template<class BX>
int get(){
return counters[IndexOf<BX>()].counter;;
}
};
C ++ 14版IndexOf
:
template<class T>
static constexpr int IndexOf()
{
// find index of T in Bs...
int toReturn = 0;
int index = 0;
using swallow = int[];
(void) swallow {0, (std::is_same<T, Bs>() ? toReturn = index : ++index, 0)...};
return toReturn;
}