使用 CRTP 实现单例
Implementing Singleton with CRTP
阅读 this answer 后,我尝试实现一些简单的 CRTP 用法。我想我会尝试实现 Singleton(是的,我知道 - 它只是为了练习和研究)模式,因为链接的答案 kind of 已经做到了......除了它不编译的事实。
引用代码如下:
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
private:
Singleton(){}
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
class A: public Singleton<A>
{
//Rest of functionality for class A
};
然后我 'modernized' 到:
template <class T>
class Singleton {
public:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T;
return *instance;
}
protected:
static inline T* instance = nullptr;
};
class A: public Singleton<A> {
//Rest of functionality for class A
};
然后我尝试创建对该实例的引用:
auto& x = A::get_instance();
显然没有编译。
值得一提的是,我收到了非常相似的错误消息,特别是:
note: 'A::A()' is implicitly deleted because the default definition would be ill-formed: class A : public Singleton<A>
.
显然,第二段代码无法编译,因为我们删除了默认构造函数并尝试在get_instance
方法中将其与new T
一起使用.
令我吃惊的是,第一个片段也没有编译,并出现类似的错误消息。链接的答案是否有错误?我将如何使用 CRTP 为单例 实现 通用基础 class / 接口?
您的第一个代码块的问题是 Singleton(){}
被标记为私有。这意味着 A
无法访问它,因此 A
不能是默认构造。使构造函数 protected
将解决
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
Singleton(){}
private:
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
class A: public Singleton<A>
{
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::GetInstance();
}
你的第二个代码块有类似的问题,但你将它标记为 delete
而不是默认构造 private
所以它不是 =default 可构造的意思 A
也将不是默认可构造的。默认构造函数正在使它 protected
就像第一个示例将修复
template <class T>
class Singleton {
public:
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T;
return *instance;
}
protected:
Singleton() = default;
static inline T* instance = nullptr;
};
class A: public Singleton<A> {
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::get_instance();
}
尽可能小的(我认为)实现。
特点:
- A 不可复制、不可构造或可移动。 (通过删除复制操作隐式删除移动操作符)
- 实现的构造是线程安全的。
- 确保在程序结束时销毁实现。
template <class T>
struct Singleton
{
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
static T& get_instance() {
static T _{allow()};
return _;
}
private:
struct allow {};
protected:
Singleton(allow) {}
};
class A: public Singleton<A> {
using Singleton<A>::Singleton;
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::get_instance();
auto& y = A::get_instance();
// compiler error
auto z = A();
}
但为什么不将 'singleton-ness' 作为实现细节呢?为什么用户需要知道该对象是单例?
template <class T>
struct Singleton
{
protected:
static T& get_impl() {
static T _;
return _;
}
};
// the real implementation of A
struct AImpl
{
void foo();
};
// A is a value-type which just happens to be implemented in terms of a
// single instance
struct A: public Singleton<AImpl>
{
auto foo() { return get_impl().foo(); }
};
void bar(A a)
{
a.foo();
}
int main()
{
auto x = A();
x.foo();
auto y = A();
y.foo();
x = y;
bar(x);
}
然后,如果您决定类型不应该是单例,则不需要更改其接口(因此也不需要更改程序的其余部分):
示例 - A 是单身人士而 B 不是。接口完全相同。
#include <memory>
template <class T>
struct Singleton
{
protected:
static T& get_impl() {
static T _;
return _;
}
};
template<class T>
struct CopyableIndirect
{
CopyableIndirect() = default;
CopyableIndirect(CopyableIndirect const& r)
: impl_(std::make_unique<T>(*r.impl_))
{
}
CopyableIndirect(CopyableIndirect&& r)
: impl_(std::move(r.impl_))
{
}
CopyableIndirect& operator=(CopyableIndirect const& r)
{
auto temp = r;
swap(temp);
return *this;
}
CopyableIndirect& operator=(CopyableIndirect && r)
{
auto temp = std::move(r);
swap(temp);
return *this;
}
void swap(CopyableIndirect& r)
{
std::swap(impl_, r.impl_);
}
protected:
T& get_impl() {
return *impl_;
}
T const& get_impl() const {
return *impl_;
}
std::unique_ptr<T> impl_ = std::make_unique<T>();
};
struct AImpl
{
void foo() const;
};
struct A: public Singleton<AImpl>
{
auto foo() const { return get_impl().foo(); }
};
struct B: public CopyableIndirect<AImpl>
{
auto foo() const { return get_impl().foo(); }
};
void bar(A const& a)
{
a.foo();
}
void bar(B const& a)
{
a.foo();
}
int main()
{
auto x = A();
x.foo();
auto y = B();
y.foo();
bar(x);
bar(y);
}
"modernized" 片段的修改:
template <class T>
class Singleton {
public:
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T_Instance;
return *instance;
}
protected:
Singleton() {}
private:
struct T_Instance : public T {
T_Instance() : T() {}
};
static inline T* instance = nullptr;
};
class A : public Singleton<A> {
protected:
A() {}
};
int main()
{
auto& x = A::get_instance();
}
代码段的更改摘要:
protected
单例中的默认构造函数
private
嵌套结构以访问派生的 class 的受保护构造函数
protected
派生中的构造函数 class 以防止实例化
此外,不需要 delete
通过向 Singleton class.
添加默认 ctor 实现而隐式删除的构造函数
不像 Richard Hodges' 示例那么小,但是静态 instance
成员可以轻松添加 delete_instance() 方法以用于自动化单元测试。
阅读 this answer 后,我尝试实现一些简单的 CRTP 用法。我想我会尝试实现 Singleton(是的,我知道 - 它只是为了练习和研究)模式,因为链接的答案 kind of 已经做到了......除了它不编译的事实。
引用代码如下:
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
private:
Singleton(){}
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
class A: public Singleton<A>
{
//Rest of functionality for class A
};
然后我 'modernized' 到:
template <class T>
class Singleton {
public:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T;
return *instance;
}
protected:
static inline T* instance = nullptr;
};
class A: public Singleton<A> {
//Rest of functionality for class A
};
然后我尝试创建对该实例的引用:
auto& x = A::get_instance();
显然没有编译。
值得一提的是,我收到了非常相似的错误消息,特别是:
note: 'A::A()' is implicitly deleted because the default definition would be ill-formed:
class A : public Singleton<A>
.
显然,第二段代码无法编译,因为我们删除了默认构造函数并尝试在get_instance
方法中将其与new T
一起使用.
令我吃惊的是,第一个片段也没有编译,并出现类似的错误消息。链接的答案是否有错误?我将如何使用 CRTP 为单例 实现 通用基础 class / 接口?
您的第一个代码块的问题是 Singleton(){}
被标记为私有。这意味着 A
无法访问它,因此 A
不能是默认构造。使构造函数 protected
将解决
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
Singleton(){}
private:
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
class A: public Singleton<A>
{
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::GetInstance();
}
你的第二个代码块有类似的问题,但你将它标记为 delete
而不是默认构造 private
所以它不是 =default 可构造的意思 A
也将不是默认可构造的。默认构造函数正在使它 protected
就像第一个示例将修复
template <class T>
class Singleton {
public:
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T;
return *instance;
}
protected:
Singleton() = default;
static inline T* instance = nullptr;
};
class A: public Singleton<A> {
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::get_instance();
}
尽可能小的(我认为)实现。
特点:
- A 不可复制、不可构造或可移动。 (通过删除复制操作隐式删除移动操作符)
- 实现的构造是线程安全的。
- 确保在程序结束时销毁实现。
template <class T>
struct Singleton
{
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
static T& get_instance() {
static T _{allow()};
return _;
}
private:
struct allow {};
protected:
Singleton(allow) {}
};
class A: public Singleton<A> {
using Singleton<A>::Singleton;
//Rest of functionality for class A
};
int main()
{
auto& x = Singleton<A>::get_instance();
auto& y = A::get_instance();
// compiler error
auto z = A();
}
但为什么不将 'singleton-ness' 作为实现细节呢?为什么用户需要知道该对象是单例?
template <class T>
struct Singleton
{
protected:
static T& get_impl() {
static T _;
return _;
}
};
// the real implementation of A
struct AImpl
{
void foo();
};
// A is a value-type which just happens to be implemented in terms of a
// single instance
struct A: public Singleton<AImpl>
{
auto foo() { return get_impl().foo(); }
};
void bar(A a)
{
a.foo();
}
int main()
{
auto x = A();
x.foo();
auto y = A();
y.foo();
x = y;
bar(x);
}
然后,如果您决定类型不应该是单例,则不需要更改其接口(因此也不需要更改程序的其余部分):
示例 - A 是单身人士而 B 不是。接口完全相同。
#include <memory>
template <class T>
struct Singleton
{
protected:
static T& get_impl() {
static T _;
return _;
}
};
template<class T>
struct CopyableIndirect
{
CopyableIndirect() = default;
CopyableIndirect(CopyableIndirect const& r)
: impl_(std::make_unique<T>(*r.impl_))
{
}
CopyableIndirect(CopyableIndirect&& r)
: impl_(std::move(r.impl_))
{
}
CopyableIndirect& operator=(CopyableIndirect const& r)
{
auto temp = r;
swap(temp);
return *this;
}
CopyableIndirect& operator=(CopyableIndirect && r)
{
auto temp = std::move(r);
swap(temp);
return *this;
}
void swap(CopyableIndirect& r)
{
std::swap(impl_, r.impl_);
}
protected:
T& get_impl() {
return *impl_;
}
T const& get_impl() const {
return *impl_;
}
std::unique_ptr<T> impl_ = std::make_unique<T>();
};
struct AImpl
{
void foo() const;
};
struct A: public Singleton<AImpl>
{
auto foo() const { return get_impl().foo(); }
};
struct B: public CopyableIndirect<AImpl>
{
auto foo() const { return get_impl().foo(); }
};
void bar(A const& a)
{
a.foo();
}
void bar(B const& a)
{
a.foo();
}
int main()
{
auto x = A();
x.foo();
auto y = B();
y.foo();
bar(x);
bar(y);
}
"modernized" 片段的修改:
template <class T>
class Singleton {
public:
Singleton& operator = (const Singleton&) = delete;
Singleton& operator = (Singleton&&) = delete;
static T& get_instance() {
if(!instance)
instance = new T_Instance;
return *instance;
}
protected:
Singleton() {}
private:
struct T_Instance : public T {
T_Instance() : T() {}
};
static inline T* instance = nullptr;
};
class A : public Singleton<A> {
protected:
A() {}
};
int main()
{
auto& x = A::get_instance();
}
代码段的更改摘要:
protected
单例中的默认构造函数private
嵌套结构以访问派生的 class 的受保护构造函数protected
派生中的构造函数 class 以防止实例化
此外,不需要 delete
通过向 Singleton class.
不像 Richard Hodges' 示例那么小,但是静态 instance
成员可以轻松添加 delete_instance() 方法以用于自动化单元测试。