CRTP Singleton 不完整类型或非文字类型
CRTP Singleton Incomplete type or Non-literal type
我正在尝试制作 CRTP 单例。这里已经有几个例子了。我不确定我的有何不同或无法编译的原因。第一次尝试:
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<Derived1>::impl’ of incomplete type
.
嗯。好吧。不确定为什么该类型不完整。尝试:
. . .
private:
static constexpr Impl impl{};
};
现在我们在 link 时间失败:undefined reference to 'Base<Derived1>::impl'
真的?!对我来说看起来已经定义和初始化了......但即使它确实 link 我有一个带有非平凡析构函数的 Derived 所以编译器将在编译时炸弹抱怨 constexpr 中使用的非文字类型。
为什么 Derived1 不完整?我该如何构建它?
不完整的类型错误是因为您在 getInstance
中使用 impl
之前它存在。
解决此问题的一种方法是在 class 定义之外初始化 impl
并确保它在使用前已初始化:
template <class Impl>
const Impl Base<Impl>::impl {};
尝试以这种方式实现您的 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);
}
到实例化 Base<Derived1>
的时间点(就在 Derived1
定义的开始处),class 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 = {};
我认为,否则为多个实现正确排序将很棘手。
我正在尝试制作 CRTP 单例。这里已经有几个例子了。我不确定我的有何不同或无法编译的原因。第一次尝试:
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<Derived1>::impl’ of incomplete type
.
嗯。好吧。不确定为什么该类型不完整。尝试:
. . .
private:
static constexpr Impl impl{};
};
现在我们在 link 时间失败:undefined reference to 'Base<Derived1>::impl'
真的?!对我来说看起来已经定义和初始化了......但即使它确实 link 我有一个带有非平凡析构函数的 Derived 所以编译器将在编译时炸弹抱怨 constexpr 中使用的非文字类型。
为什么 Derived1 不完整?我该如何构建它?
不完整的类型错误是因为您在 getInstance
中使用 impl
之前它存在。
解决此问题的一种方法是在 class 定义之外初始化 impl
并确保它在使用前已初始化:
template <class Impl>
const Impl Base<Impl>::impl {};
尝试以这种方式实现您的 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);
}
到实例化 Base<Derived1>
的时间点(就在 Derived1
定义的开始处),class 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 = {};
我认为,否则为多个实现正确排序将很棘手。