3

是否可以在类中创建非静态模板字段?
如果没有,如何解决?

此类字段应根据需要在编译时创建。

例子

我有很多 -B类,如B1, B2, B3.
(在实际情况下,它们有更有意义的名称。)

我想创建一个D具有非静态模板函数的类add<BX>()counter++每次我调用它时,对于每个 individual BX,对于 D 的某个实例都必须这样做。
(在实际情况下,它会做一些更复杂的事情。)

这是一个实现它的工作演示。
可悲的是,我目前必须在里面对每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,但是对于非静态的,这似乎是不可能的。

4

3 回答 3

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;
    }
};

这与您真正想要的非常接近(顺便说一句,您走在正确的轨道上)。

于 2017-05-17T22:05:24.607 回答
4

不幸的是,在我们对标准进行反思之前,没有简单的方法可以迭代类的成员。

在那之前的解决方案要么涉及自己实现反射(很难并且经常使用带有自身问题的宏,例如可调试性),要么将您的类型包装成另一种类型(更容易)。

我们可以使用具有一个std::arrayCounters 的基类来做到这一点,每个 Counters 一个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;
}

C++14 演示

于 2017-05-17T11:54:55.500 回答
4

使用(来自 Yakk 的建议;谢谢)索引和 RTTI?std::map std::unordered_map

#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;
 }
于 2017-05-17T11:33:01.137 回答