Suppose you have a private nested type. You might use this because you need your constructor to be public in order to work with some framework,¹ but you don’t want people to do their own make_
unique
; you want them to go through your factory.
class Package { struct private_constructor { }; public: // Do not call constructor directly. Use CreatePackage instead. Package(int id, private_constructor); static Package CreatePackage(int id, int flavor) { Package package(id, private_constructor()); ... do other stuff that gets the package ready ... return package; } }; void bad_boy() { // This doesn't work. Wrong number of parameters. Package package(3); // This doesn't work. private_constructor is a private type. Package package(3, Package::private_constructor()); }
But C++11 introduced braced initialization, and the bad boy can use that to construct the type without naming it.
void bad_boy_got_through() { // Bad boy uses empty braces to sneak past the gate! Package package(3, {}); }
To prevent this, you need to give your private type an explicit constructor so it cannot be used implicitly.
class Package
{
struct private_constructor
{ explicit private_constructor() = default; };
public:
// Do not call constructor directly. Use CreatePackage instead.
Package(int id, private_constructor);
...
};
With this change, the bad boy has been foiled.
void bad_boy_foiled() { // Can't sneak in with empty braces. Package package(3, {}); }
From Visual C++:
error C2664: 'Package::Package(Package &&)': cannot convert argument 2 from 'initializer list' to 'Package::private_constructor'
From clang:
error: converting to 'Package::private_constructor' from initializer list would use explicit constructor 'constexpr Package::private_constructor::private_constructor()'
And the explicit constructor is inaccessible.
void bad_boy_foiled() { // Can't use explicit constructor. Package package(3, Package::private_constructor{}); } // error: cannot access private struct
¹ For example, std::
make_
unique
requires that the object have a public constructor.
C# has a problem similar to this and I hate it. Every C# struct has a default parameterless public constructor that just zeroes everything out. But I need my structs initialized a certain way. If I accidentally call new() instead of a factory method in the app code then I’lI have bugs, but I can’t protect against this.