【问题标题】:Non-template static counter in template模板中的非模板静态计数器
【发布时间】:2012-09-28 12:26:11
【问题描述】:

我有一个模板 Singleton 类,可用于我的代码的一些重要组件。使用单例代码模型不是这个问题的重点。

现在,我想在这个类中添加一个静态计数器,由使用这个模板的每个类共享。让我为您编写代码(代码并不详尽):

template <class T>
class Singleton
{
public:
   Singleton(const std::string &name){ 
      printf("%s CTOR call #%d\n", name.c_str(), _counter);  
      _counter++; 
   }
   virtual ~Singleton(){}
private:
   static int _counter; // I want this to be shared by all classes
}

// I can only initialize it like this; sadly
template<class T> 
int Singleton<T>::_counter = 0;

// main code (simplified):
Singleton<MyClass1>("MyClass1") c1;
Singleton<MyClass2>("MyClass2") c2;
Singleton<MyClass3>("MyClass3") c3;
Singleton<MyClass4>("MyClass4") c4;

预期输出:

MyClass1 CTOR call #0
MyClass2 CTOR call #1 // counter is incremented
MyClass3 CTOR call #2
MyClass4 CTOR call #3

我得到的是:

MyClass1 CTOR call #0
MyClass2 CTOR call #0 // counter is not incremented
MyClass3 CTOR call #0
MyClass4 CTOR call #0

这意味着静态 int 不是共享的,而是特定于每个类的。

如何在我的模板类中有一个“非模板化”计数器?仅标题模板可以做到这一点吗?

【问题讨论】:

    标签: c++ templates static


    【解决方案1】:

    您可以为 Counter 使用内部 static 变量创建一个单独的类。所以这更像是“嵌套静态”计数:)。

    对于语法糖,重载 opeartor ++ 和任何你需要的东西。

    以下是这样做的主要方法:

    struct Counter
    {
      static unsigned int value;
      Counter& operator ++ (int) { value ++; return *this; }
      operator unsigned int () const { return value; }
    };
    unsigned int Counter::value = 0;
    
    template <class T>
    class Singleton
    {
    public:
       Singleton(const std::string &name){ 
          printf("%s CTOR call #%d\n", name.c_str(), s_counter.value);  
          s_counter++; 
       }   
       virtual ~Singleton(){}
    private:
       static Counter s_counter; // shared by all classes
    };
    

    这是working demo

    更新:这是另一种“强制”header only template 文件的方式,您不必在单独的 .cpp 文件中定义 Counter::value;但是我更喜欢上面的。

    template<bool _true>
    struct Counter
    {
      static unsigned int value;
      Counter& operator ++ (int) { value ++; return *this; }
      operator unsigned int () const { return value; }
    };
    template<bool _true>
    unsigned int Counter<_true>::value = 0;
    
    template<>
    struct Counter<false>; // disable the other alternative so no one can invoke it
    
    template <class T>
    class Singleton
    {
    public:
       Singleton(const std::string &name){ 
          printf("%s CTOR call #%d\n", name.c_str(), s_counter.value);  
          s_counter++; 
       }   
       virtual ~Singleton(){}
    private:
       static Counter<true> s_counter; // shared by all classes
    };
    template<class T>
    Counter<true> Singleton<T>::s_counter;
    

    【讨论】:

    • 问题是我必须提供一个 cpp 文件来放置值实例。我想我无法克服这个事实......
    • 在我的设置中,您的解决方案没有链接,因为每次包含“Singleton.h”文件时,我都会定义多个unsigned int Counter::value = 0; 实例。您的测试正在运行,因为其中不涉及任何头文件。为了编译它,我需​​要将value 包含在一个成员函数中,这最终归结为 Nim 的替代解决方案。
    • @Gui13,在 .cpp 文件中定义 Counter::value 有什么问题?如果您只想将其设为头文件,那么将Counter 设为虚拟template 类是微不足道的; see the solution。但我总是更喜欢前者。有一个单独的类来管理计数器可能会帮助您在未来的增强和更易于维护。
    【解决方案2】:

    您可以将计数器放在单独的类中,并将其设为模板,因为您只需要标题。示例:

    template <class T> 
    struct Helper
    {
      static int _counter;
    };
    
    template<class T>
    int Helper<T>::_counter = 0;
    
    struct Dummy{};
    
    template <class T> 
    class Singleton 
    { 
    public: 
       Singleton(const std::string &name){  
         printf("%s CTOR call #%d\n", name.c_str(), Helper<Dummy>::_counter);   
         Helper<Dummy>::_counter++;  
       } 
       virtual ~Singleton(){} 
    };
    

    edit 与 Nim 的解决方案相比,这当然太复杂了.. 但将其视为学习模板的一种实践

    【讨论】:

    • 不 - 我的解决方案很愚蠢,我回答了错误的问题...为您的问题 +1... :)
    【解决方案3】:

    如下:

    static int& counter()
    {
      static int _counter = 0;
      return _counter;
    }
    

    使 this 成为你的单例的成员,而不是成员变量,使用 this...

    编辑:我刚刚重新阅读了您的问题,您需要将其设为另一种类型的成员,例如:

    struct Counter
    {
       static int& counter()
       {
         static int _counter = 0;
         return _counter;
       }
    };
    
    template <class T>
    class Singleton
    {
    public:
       Singleton(const std::string &name){ 
          std::cout << name <<Counter::counter() << std::endl;
          Counter::counter()++;
       }
       virtual ~Singleton(){}
    private:
    };
    

    【讨论】:

    • 您的替代解决方案有效。我会接受你的选择,因为 stijn 有点复杂(我都赞成:))
    猜你喜欢
    • 2012-03-02
    • 1970-01-01
    • 2019-06-21
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-06
    相关资源
    最近更新 更多