Trying to allow make_unique
on a class with private ctor I came into the following strange difference between two cases:
class A {
int _i;
A(): _i(7) {}
public:
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
struct enablePrivateCtor : public A {
using A::A;
};
return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
}
void doIt() const {
std::cout << _i << std::endl;
}
};
int main() {
auto a = A::create();
a->doIt();
}
Output:
7
class A {
int _i;
A(int i): _i(i) {} // <- change 1, ctor getting int
public:
// no change here!
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
struct enablePrivateCtor : public A {
using A::A;
};
return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
}
void doIt() const {
std::cout << _i << std::endl;
}
};
int main() {
auto a = A::create(7); // <- change 2, sending 7
a->doIt();
}
Compilation Error:
unique_ptr.h: error: calling a private constructor of class 'enablePrivateCtor'
Why the 1st one - with the empty ctor - is OK, while the 2nd - the non-empty ctor - is not?
The default constructor is never inherited. Therefore, the first enablePrivateCtor
generates a default constructor, which calls the base class default constructor.
When you inherit a constructor (as in the second case), the new constructor has the same access level as the inherited one. So since A::A(int)
is private, so too will be enablePrivateCtor::enablePrivateCtor(int)
. So you won't be able to construct with it.
If you need to have a private constructor be able to be called indirectly (through make_unique
/emplace
/etc), then you need to use a private key type. Like this:
class A;
class A_key
{
A_key() = default;
A_key(int) {} //Prevent `A_key` from being an aggregate.
friend class A;
};
class A {
int _i;
public:
A(int i, A_key): _i(i) {}
// no change here!
template<typename... T>
static std::unique_ptr<A> create(T&&... t)
{
return std::make_unique<A>(std::forward<T>(t)..., A_key{});
}
void doIt() const {
std::cout << _i << std::endl;
}
};
...
auto ptr = A::create(7);
A a(7, A_key{}); //Does not compile, since you're not a friend.
A_key
is publicly copyable, but it is not publicly default constructible. So non-private code can pass them around, but non-private code cannot create them.
The difference is that enablePrivateCtor
automatically gets a default constructor (which is allowed to call A::A
).
It doesn't automatically get an integer conversion constructor: add
enablePrivateCtor(int i) : A(i) {}
and see it work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With