【问题标题】:Explicit template static member instantiation in a derived class派生类中的显式模板静态成员实例化
【发布时间】:2015-01-15 13:29:12
【问题描述】:

我正在尝试使用静态成员实现模板类。从模板类派生的类无需编写额外代码即可实例化。

这是我幼稚(但并不成功)的方法:

Singleton.h:

template <class T> class Singleton {
protected:
  Singleton();
  static T instance_;
}

// explicit instantiation of 'instance_' ???, 
// where 'instance_' is an instance of the derived class
template <class T> T Singleton<T>::instance_;

ConcreteA.h:

class ConcreteA : public Singleton<ConcreteA> {
public:
  ConcreteA();
  void foo();
}

main.c:

int main() {
  // an instance of ConcreteA should have been created (no extra code)!!!
  return 0;
}

有没有办法通过从Singleton 派生ConcreteA 来强制实例化ConcreteA,而无需编写额外的实例化代码?

一个肮脏的解决方法是在ConcreteA构造函数中调用instance_上的方法,例如:

混凝土A.c

ConcrereA::ConcreteA { instance_.foo(); }

有更好的解决方法吗?

【问题讨论】:

    标签: c++ templates c++11 inheritance singleton


    【解决方案1】:

    建立在您自己的“肮脏技巧”之上,这适用于我测试的所有编译器,并且不需要派生类构造函数中的任何代码:

    #include <iostream>
    
    template <class T> class Singleton {
    protected:
      Singleton() { instptr_ = &instance_; }
      static T instance_;
    private:
      static T* instptr_;
    };
    
    template<class T> T Singleton<T>::instance_;
    template<class T> T* Singleton<T>::instptr_;
    
    class ConcreteA : public Singleton<ConcreteA> {
    public:
      ConcreteA() { std::cout << "ConcreteA constructed.\n"; }
      void foo();
    };
    
    int main() {
      //Prints 'ConcreteA constructed.'.
      return 0;
    }
    

    我的理解是,取instance_的地址odr-uses,强制它存在。我必须说我不是 100% 确定这保证不会在某些编译器的未来版本中被优化掉(我在任何地方都使用 -O2 对其进行了测试)。

    编辑:看起来甚至像这样编写基类构造函数

    Singleton() { (void)&instance_; }
    

    足够了,它完全摆脱了instptr_

    【讨论】:

    • 是的,投射到void 似乎是这里最小的解决方案。这是摆脱无法修复的“变量/参数未使用”警告的常用方法。
    • @BartoszKP 是的,在我写完第一个变体后我立即想到了这一点,但我决定将第一个变体也保留在那里,因为它看起来不太可能被优化掉(我仍然对这部分有疑问,编译器现在使用的所有“整个程序/链接时优化”)。
    • 有没有办法让ConcreteA构造函数protected
    • @sergej 您必须将friend Singleton&lt;ConcreteA&gt;; 添加到ConcreteA 的定义中。这样就可以了,因为instance_ 的初始化是在Singleton&lt;T&gt; 的上下文中,它现在可以访问受保护的构造函数。
    【解决方案2】:

    很遗憾,这是不可能的。引用 C++11 14.7.1/2(谈论模板的隐式实例化):

    除非类模板或成员模板的成员已被显式实例化或显式 特化,成员的特化在引用特化时隐式实例化 在需要成员定义存在的上下文中; 尤其是初始化(以及任何相关的 除非静态数据成员本身以某种方式使用,否则不会发生静态数据成员的副作用) 这需要静态数据成员的定义存在。

    【讨论】:

      【解决方案3】:

      这样做是这样的:

      template <class T> class Singleton {
      protected:
        Singleton();
      
        // note: static function contains static variable
        static T& instance() {
          static T _t; // c++11 guarantees that this is thread-safe
          return _t;
        }
      }
      
      class ConcreteA : public Singleton<ConcreteA>
      {
        ...
      };
      
      auto& myA = ConcreteA::instance();
      

      【讨论】:

      • 感谢您的回答:auto&amp; myA = ConcreteA::instance(); 是我要避免的行。
      • 除了最令人费解的情况外,避免这条线是错误的 - 你会让用户陷入困境。
      【解决方案4】:

      在 C++ 中,静态对象保证只有在定义实例的文件中的代码被调用时才会初始化(不保证初始化的顺序)。

      因此,最好在函数中实例化对象,而不是调用函数

      class A{
      public:
          static A& getInstance(){
             static A a;
             return a;
          }
      protected:
          A(){
          }
      };
      

      【讨论】:

      • @user2139223 它现在可以编译,但仍然: 1) 是错误的(通常您不想复制单例,而您的 getInstance 会这样做)。 2)即使把它放在一边也不能回答问题。
      • 我已经回答了第一部分的问题,如果我不够清楚,对不起
      猜你喜欢
      • 1970-01-01
      • 2011-04-11
      • 2017-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多