Reputation: 9359
I am using GCC 4.8 to compile the following code:
#include <memory>
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args&& ...args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
struct S {
template<class... Args>
static std::unique_ptr<S> create(Args&&... args) {
return make_unique<S>(std::forward<Args>(args)...);
}
private: // if I remove this line, then the compilation is OK
S(int) {}
S() = default;
};
int main() {
auto s1 = S::create(); // OK
auto s2 = S::create(0); // Compilation error
}
Can anyone explain me the reason of this error from the compiler?
main.cpp: In instantiation of 'std::unique_ptr make_unique(Args&& ...) [with T = S; Args = {int}]':
main.cpp:11:58: required from 'static std::unique_ptr S::create(Args&& ...) [with Args = {int}]'
main.cpp:20:26: required from here
main.cpp:14:5: error: 'S::S(int)' is private
S(int) {} ^
main.cpp:5:65: error: within this context return std::unique_ptr(new T{std::forward(args)...});
^
Upvotes: 9
Views: 1113
Reputation: 361322
Can anyone explain me the reason of this error from the compiler?
The constructor which takes int
is declared to be private
which is why it is giving compilation error. Note that the constructor is getting invoked from make_unique
(which doesn't have access to private members), not from create
.
However, you're probably wondering why the first call to to create()
compiles fine, I think it is because GCC has bug. It should not compile even in this case, because the default constructor is declared to be private
as well. Clang correctly gives error for both calls (see this).
Anyway, if you want to keep them private
, then make make_unique
a friend of the class.
Upvotes: 12
Reputation: 45414
If you want to keep the class's constructors private, you must make any non-member user (here: make_unique
) a friend:
struct S {
template<typename T, typename ...Args>
friend std::unique_ptr<T> make_unique(Args&& ...args);
// rest as before
};
Alternatively, you may avoid make_unique<>
and directly create the unique_ptr<S>
from a static member:
struct S {
template<class... Args>
static std::unique_ptr<S> create(Args&&... args)
{ return std::unique_ptr<S>(new S{std::forward<Args>(args)...}); }
// rest as before
};
Upvotes: 2
Reputation: 11116
The reason is fairly simple:
The constructor is not called from within S::create
, but from within the ::make_unique
function template, which does not have access to the private member function S::S(int)
.
A simple fix would be to call new
yourself (see here).
Actually, the more interesting question is why it does not error on the first call as well...
Upvotes: 4
Reputation: 3691
If some constructor is private, it means that no one but the class itself (and friends) should be able to create instances of it using that constructor.
To create instance of a class which has only privates constructors, you have to use a static method.
Upvotes: 1
Reputation: 7118
In C++ struct, all members are public by default. In a class declaration, members are private by default. In your case, constructors have been made private, that's why you get the error: S::S(int) was private
So, make the changes as:
public:
S(int) {}
S() = default;
Upvotes: 2