非静态模板成员:可能吗?
Posted
技术标签:
【中文标题】非静态模板成员:可能吗?【英文标题】:non-static template member : possible? 【发布时间】:2017-10-16 16:40:13 【问题描述】:是否可以在类中创建非静态模板字段? 如果没有,如何解决?
此类字段应在编译时根据需要创建。
示例
我有很多B
-class,比如B1
,B2
,B3
。(实际上,它们有更有意义的名字。)
我想创建一个类D
,它具有非静态模板函数add<BX>()
,每次我调用它时都必须counter++
,对于每个人BX
,对于一个D 的某个实例。(在实际情况下,它做了更复杂的事情。)
这是一个有效的demo 来实现它。
可悲的是,我目前必须在D
中逐个(B1
、B2
、B3
)对每个BX
进行硬编码:-
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
是静态的,我知道该怎么做,但对于非静态来说,这似乎是不可能的。
【问题讨论】:
【参考方案1】:您不需要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;
;
这与您真正想要的非常接近(顺便说一句,您走在正确的轨道上)。
【讨论】:
它是如此简单......非常好的和优雅的解决方案。 只是给自己的一个提示:在这个解决方案中,我必须手动将所有BX
放入 D
例如D<B1,B2,B3> d1;
有点像 AndyG 的解决方案。
@javaLover 不幸的是。【参考方案2】:
使用索引和 RTTI 的 std::map
std::unordered_map
(Yakk 的建议;谢谢)?
#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;
【讨论】:
你关心那张地图的顺序吗? Yakk - 不,我想我不知道。你的意思是更好std::unordered_map
?
出色的 RTTI 解决方案!
它比 OP 慢 2 倍,不过 (-O2)。 OP 的代码(~ 60K 滴答声):coliru.stacked-crooked.com/a/f3d9885b523dc9a5 这个解决方案(~120K 滴答声):coliru.stacked-crooked.com/a/4fe76fc73507a306。这还不错 - 我只想指出。
@javaLover 它做的事情与 OP 不同:OP 代码处理固定的类型集合;这处理了无限的类型集合。另请注意,该操作是微不足道的(++),而在“真实”情况下,该操作将更加昂贵(因此比率会下降)。而max66,是的,map
真的是ordered_map
:如果你不需要订单,考虑unordered_map
。【参考方案3】:
不幸的是,在我们对标准进行反思之前,没有简单的方法可以迭代类的成员。
在此之前的解决方案要么涉及自己实现反射(很难并且经常使用带有自身问题的宏,例如可调试性),要么将您的类型包装成另一种类型(更容易)。
我们可以使用具有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;;
;
Live Demo
编辑:
IndexOf
的C++14版本:
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
【讨论】:
你一如既往地带着巨大的演示。 :) @javaLover 就我个人而言,当我看到一个帖子时,我通常想直接跳到可运行的代码中,所以我想其他人也会这样做。我非常高兴你的问题有这个。 注意这个问题被标记为 C++14。 @Yakk 我错过了。 OP 的链接示例使用 C++17以上是关于非静态模板成员:可能吗?的主要内容,如果未能解决你的问题,请参考以下文章