c++ - 单例,奇怪的重复模板模式和转发构造函数参数

标签 c++ templates c++11 singleton variadic-templates

好的,我知道应该避免使用单例,但是很少有真正需要单例的情况。所以我的解决方案使用 CRTP(奇怪的重复模式)实现它们,如下所示:

#include <iostream>
#include <utility>

using namespace std;

template<typename T> // Singleton policy class
class Singleton
{
protected:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
public:
    template<typename... Args>
    static T& getInstance(Args... args) // Singleton
    {
        // Guaranteed to be destroyed.
        // Instantiated on first use.
        // Thread safe in C++11
        static T instance{std::forward<Args>(args)...};
        return instance;
    }
};

class Foo: public Singleton<Foo>
{
    friend class Singleton<Foo>;
    Foo()
    {
        cout << "Constructing instance " << this <<" of Foo" << endl;
    }
    Foo(int x)
    {
        cout << "Constructing instance " << this <<" of Foo with argument x = "\
             << x << endl;
    }
    ~Foo()
    {
        cout << "Destructing instance " << this << " of Foo" << endl;   
    }
public:
    // public 
};

int main()
{
    Foo& rfoo = Foo::getInstance(); // default constructible

    // this should just return the instance
    // instead, it constructs another instance
    // because invokes an overloaded version of get_instance()  
    Foo& rfoo1 = Foo::getInstance(1); 

    // this is OK
    // calls the SAME overloaded version again, instance is static
    // so we get the same instance
    Foo& rfoo2 = Foo::getInstance(2); 
}

如您所见,我允许从具有重载/非默认构造函数的类中创建单例。但这又让我反感,因为通过使用可变参数模板化的 get_instance() 函数并通过 std::forward 传递实例参数,编译器会生成重载对于具有不同类型的每个调用,因此为每个重载返回一个新的静态实例。我想要的是通过引用返回一个以某种方式对所有可能的重载都是通用的单个实例,但无法弄清楚如何去做。任何想法如何去做?谢谢!

PS:我可以使用指针而不是引用来实现解决方案,但我更喜欢引用解决方案,因为它是线程安全的,而且在我看来更优雅。

最佳答案

对不起,我终于有时间了。你可以试试这个:

#include <iostream>
#include <utility>
#include <functional>

using namespace std;

template<typename T> // Singleton policy class
class Singleton
{
protected:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    virtual ~Singleton() = default;
public:
    template<typename... Args>
    static T& getInstance(Args... args) // Singleton
    {
        cout << "getInstance called" << std::endl;

        //we pack our arguments in a T&() function...
        //the bind is there to avoid some gcc bug
        static auto onceFunction =  std::bind( createInstanceInternal<Args...>, args... ); 
        //and we apply it once...
        return apply( onceFunction );
    }

private:

    //This method has one instance per T 
    //so the static reference should be initialized only once 
    //so the function passed in is called only the first time
    static T& apply( const std::function<T&()>& function  )
    {
        static T& instanceRef = function();
        return instanceRef;
    }

    //Internal creation function. We have to make sure it is called only once...
    template<typename... Args>
    static T& createInstanceInternal(Args... args)
    {
        static T instance{ std::forward<Args>(args)... };
        return instance;
    }
};

IdeOne 链接:

http://ideone.com/Wh9cX9

编辑(线程安全和设计问题):

据我所知,这在 C++11 中应该是线程安全的。 instance 和 instanceRef 都是静态局部变量,应该只进行一次线程安全初始化。但是,根据您的编译器,C++11 线程安全初始化可能未按照标准指定的方式实现。如果是这种情况,您可以在 apply 内明确同步作为临时解决方法。在我看来,客户端代码可以在不带参数和带参数的情况下调用 getInstance 仍然令人不安。如果客户端代码使用参数调用,那么很可能它期望/需要使用给定参数初始化单例。提供不止一种初始化可能性将导致客户端代码出现意外/不自然的行为。那可不好。如果 Foo 只有一个 parameterizad ctor 应该没问题。但是,这意味着您总是必须传递一些参数才能获取实例……最后,将单例 getInstance 参数化会导致比它解决的问题更多的问题。话虽这么说,我根本无法抗拒智力挑战……:-)。

关于c++ - 单例,奇怪的重复模板模式和转发构造函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24964769/

相关文章:

C++ 查看数据列

c++ - cocos2d-x 每次需要时制作 Sprite 或只是改变其纹理的最佳做法是什么?

templates - 使用 EditorFor<> 呈现下拉列表

templates - 根据组更改 Ansible 模板中的变量

c++ - 获取要使用的 move 构造函数/赋值运算符

c++ - 如何部分重载 C++ 模板子类中的虚函数?

c++ - C++(Windows)创建独立进程

arrays - 在字典中列出作为模板定义

c++ - 通过 union 非原子访问原子

c++ - 如何修改参数包的每个元素,并从中创建一个元组?