非静态模板成员:可能吗?
non-static template member : possible?
是否可以在 class 中创建非静态模板字段?
如果不是,如何解决?
这些字段应根据需要在编译时创建。
例子
我有很多B
-class,比如B1
,B2
,B3
.
(实际情况下,他们的名字更有意义。)
我想创建一个 class D
具有 非静态 模板函数 add<BX>()
必须 counter++
每次我调用它时,对于每个人 BX
,对于 D.
的某个实例
(在实际情况下,它做的事情更复杂。)
这是实现它的工作 demo。
可悲的是,我目前必须在 D
中对每个 BX
、一个一个(B1
、B2
、B3
)进行硬编码:-
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
是静态的,我知道怎么做,但对于非静态的,这似乎是不可能的。
使用 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;
}
不幸的是,在我们对标准进行反思之前,将没有简单的方法来迭代 class 的成员。
在那之前的解决方案要么涉及自己实现反射(很难并且经常使用带有自身问题(如可调试性)的宏),要么将您的类型包装成另一种类型(更容易)。
我们可以用一个有 std::array
个计数器的基 class 来做到这一点,每个 BX
:
template<class... Bs>
struct Base
{
std::array<Counter, sizeof...(Bs)> counters;
// ... more on this later
};
然后我们的 D
class 可以从中派生并得到它需要的计数器:
struct D : Base<B1, B2, B3>{ /*...*/};
接下来我们要做的是在基础 class 中实现一个 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
class 大大简化了,不依赖于标签调度:
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;;
}
};
Live Demo
编辑:
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;
}
C++14 Demo
你不需要 RTTI
来解决这个问题,也不需要 std::map
,它们非常昂贵(特别是 RTTI)。 Variadic 模板和继承可以为您解决这个问题:
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;
}
};
这与您实际想要的非常接近(顺便说一句,您走对了路)。
是否可以在 class 中创建非静态模板字段?
如果不是,如何解决?
这些字段应根据需要在编译时创建。
例子
我有很多B
-class,比如B1
,B2
,B3
.
(实际情况下,他们的名字更有意义。)
我想创建一个 class D
具有 非静态 模板函数 add<BX>()
必须 counter++
每次我调用它时,对于每个人 BX
,对于 D.
的某个实例
(在实际情况下,它做的事情更复杂。)
这是实现它的工作 demo。
可悲的是,我目前必须在 D
中对每个 BX
、一个一个(B1
、B2
、B3
)进行硬编码:-
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
是静态的,我知道怎么做,但对于非静态的,这似乎是不可能的。
使用 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;
}
不幸的是,在我们对标准进行反思之前,将没有简单的方法来迭代 class 的成员。
在那之前的解决方案要么涉及自己实现反射(很难并且经常使用带有自身问题(如可调试性)的宏),要么将您的类型包装成另一种类型(更容易)。
我们可以用一个有 std::array
个计数器的基 class 来做到这一点,每个 BX
:
template<class... Bs>
struct Base
{
std::array<Counter, sizeof...(Bs)> counters;
// ... more on this later
};
然后我们的 D
class 可以从中派生并得到它需要的计数器:
struct D : Base<B1, B2, B3>{ /*...*/};
接下来我们要做的是在基础 class 中实现一个 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
class 大大简化了,不依赖于标签调度:
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;;
}
};
Live Demo
编辑:
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;
}
C++14 Demo
你不需要 RTTI
来解决这个问题,也不需要 std::map
,它们非常昂贵(特别是 RTTI)。 Variadic 模板和继承可以为您解决这个问题:
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;
}
};
这与您实际想要的非常接近(顺便说一句,您走对了路)。