【问题标题】:CRTP Singleton Incomplete type or Non-literal typeCRTP Singleton 不完整类型或非文字类型
【发布时间】:2020-02-14 14:05:15
【问题描述】:

我正在尝试制作 CRTP Singleton。这里已经有几个例子了。我不确定我的有何不同或为什么无法编译。第一次尝试:

template<class Impl>
class Base
{
public:
  static const Impl& getInstance();
  static int foo(int x);
private:
  static const Impl impl{};
};
template<class Impl> inline
const Impl& Base<Impl>::getInstance()
{
  return impl;
}
template<class Impl> inline
int Base<Impl>::foo(int x)
{
  return impl.foo_impl(x);
}

class Derived1 : public Base<Derived1>
{
public:
  int foo_impl(int x) const;
};
int Derived1::foo_impl(int x) const
{
  return x + 3;
}

int main(int argc, char** argv)
{
  const Derived1& d = Derived1::getInstance();
  std::cout << Derived1::foo(3) << std::endl;
  return 0;
}

g++ 7.4.0 告诉我:error: in-class initialization of static data member ‘const Derived1 Base&lt;Derived1&gt;::impl’ of incomplete type.

嗯。那好吧。不知道为什么该类型不完整。试试:

 . . .
 private:
   static constexpr Impl impl{};
 };

现在我们在链接时失败了:undefined reference to 'Base&lt;Derived1&gt;::impl' 真的?!对我来说看起来已经定义并初始化了......但即使它确实链接了我有一个带有非平凡析构函数的 Derived,所以编译器会在编译时炸弹,抱怨 constexpr 中使用的非文字类型。

为什么 Derived1 不完整?我该如何构建它?

【问题讨论】:

    标签: c++ static singleton crtp


    【解决方案1】:

    不完整类型错误来自您在impl 存在之前在getInstance 中使用的事实。

    解决此问题的一种方法是在类定义之外初始化impl,并确保在使用之前对其进行初始化:

    template <class Impl>
    const Impl Base<Impl>::impl {};
    

    【讨论】:

      【解决方案2】:

      尝试以这种方式实现您的getInstance 函数:

      template <class Impl>
      inline const Impl& Base<Impl>::getInstance() {
          static const Impl impl{};
          return impl;
      }
      

      然后在foo函数中

      template <class Impl>
      inline int Base<Impl>::foo(int x) {
          return getInstance().foo_impl(x);
      }
      

      Demo

      【讨论】:

        【解决方案3】:

        Base&lt;Derived1&gt; 被实例化的时间点(就在Derived1 的定义的开头),Derived1 类是不完整的,因为直到它的声明结束为止。在 CRTP 中确实不可能有一个完整的类型,因为在您声明其继承之前,派生类型永远不会是完整的。

        对于非静态数据成员,唯一的方法是使用某种指向不完整类型的指针(很可能是std::unique_ptr)。对于静态成员,这也可以,但也可以只拆分静态成员的声明和定义。所以不是

        template<Impl>
        struct Base {
           static Impl impl{};
        };
        

        template<Impl>
        struct Base {
            static Impl impl;
        };
        

        并像这样定义它

        template<Impl>
        static Base<Impl>::impl ={};
        

        Derived1 完成后。 (请注意,我不确定这对私有静态成员是如何工作的)。在我看来,这将是最干净的,如果每个实现都为自己执行此操作,即在 Derived1 完成后添加

        template<>
        static Base<Derived1>::impl = {};
        

        我认为,否则为多个实现设置正确的顺序会很棘手。

        【讨论】:

          猜你喜欢
          • 2016-05-27
          • 2012-05-20
          • 1970-01-01
          • 1970-01-01
          • 2011-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-06
          相关资源
          最近更新 更多