Reputation: 161
All,
I am using C++14 and am making a more-or-less standard Singleton. I am using the latest Visual Studio 2017. This code works:
#include <memory>
class A
{
public:
static A& getInstance()
{
if (instance == nullptr)
instance = std::unique_ptr<A>(new A());
return *instance;
}
private:
A() {}
static std::unique_ptr<A> instance;
};
std::unique_ptr<A> A::instance = nullptr;
However, when I change the creation of the singleton instance to this:
instance = std::make_unique<A>();
I get a compilation error that I am trying to access a private member:
Error C2248 'A::A': cannot access private member declared in class 'A'
c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.14.26428\include\memory 2510
This feels like a bug to me, as the two forms should be identical in function? Thoughts?
Upvotes: 4
Views: 5689
Reputation: 778
To sum up the others answers' and fix some flaws in them: You can make a private structure with a private constructor which is a friend with your class. Then make your class constructor public but with an additional argument of that private structure.
Also its better to use static function witch return a reference instead of the bare static variable.
#include <memory>
class A
{
struct Private
{
friend A;
private:
explicit Private() = default;
};
public:
static A * getInstance()
{
if (!instance())
instance() = std::make_unique<A>(Private());
return instance();
}
A(Private) {};
private:
static std::unique_ptr<A> & instance()
{
static std::unique_ptr<A> inst;
return inst;
}
};
Or if you really dont need any special configurations which requires using a pointer and heap allocation (like initializing the instance in a special thread or ...) :
class A
{
public:
static A & getInstance()
{
static A instance;
return instance;
}
private:
A() = default;
};
Upvotes: 0
Reputation: 275395
instance = std::make_unique<A>();
this creates the A
within the function make_unique
. But the ctor you want to call is private.
private:
struct ctor_permission_t{
explicit ctor_permission_t(int){};
};
public:
explicit A(ctor_permission_t):A(){}
};
then
instance = std::make_unique<A>(ctor_permission_t{0});
The ctor_permission_t
acts as a token giving its possessor the right to construct an A
. We pass this to make_unique
.
The explicit int
ctor in ctor_permission_t
makes it impossible to create it without naming the type, and only within instances and friends of A
can name it because it is private. This makes it difficult to bypass this permission token.
Upvotes: 4
Reputation: 5026
The purpose of std::unique_ptr<>
is to control the lifetime of the object pointed to. You may pass a std::unique_ptr<>
around, but that will also transfer ownership of the object pointed to. This concept does not match very well with the concept of a singleton. There is only one place that is ever allowed to create (or delete) the singleton. You don't really need a std::unique_ptr<>
for that. As already said in the comments, there are simpler ways for that. I would prefer @Praetorian's suggestion:
static A& getInstance()
{
static A instance;
return instance;
}
The reason why you can't use std::make_unique<>()
to instantiate your class is that the constructor is private. To access it, you need the accessing function to be a friend
. Have a look at How to make std::make_unique a friend of my class for that. Another solution is to provide a public constructor requiring a private type as argument, as described int this solution.
Upvotes: 6