I mostly work on system-level C++ projects that don\'t allow exceptions to be thrown, but RAII is (rightfully) strongly encouraged. Right now, we handle the lack of failing cons
If you want to make any indirect object construction work (emplace
in its various forms, in_place
constructors of optional
, make_shared
, etc) , the constructor in question must be public. You can make a constructor public without allowing all public use by using something called a private key.
Basically, you create a type (call it Key
) whose default constructor is private. The class has no members, nor does it do anything. It declares that MyType
is a friend of Key
; this means that only members of MyType
can construct one.
Now, make all of MyType
's constructors public
, but they all take a Key const&
as the first parameter. This means that in theory anyone could call them, but in practice only someone who has a Key
instance can actually call them. Members of MyType
can create such an instance, and they can pass those instances to optional
's in_place
constructor or any other indirect mechanism. This effectively gives the indirect construction mechanism private access to the constructor.
This is a standard idiom for dealing with forwarding of private access to a type. Indeed, one could hypothetically write a generic key<T>
type like this:
template<typename T>
class key
{
private:
key() = default;
key(int) {} //Not an aggregate
friend T;
};
One small note. Because of an annoyance of C++11 pre-C++20, any type with no members and no constructors other than defaulted/deleted copy/move/default constructors is considered an aggregate. This is true even if you explicitly = default
its default constructor. As such, that type can undergo aggregate initialization, which has no public/private distinction. That is, anybody could call your private-key constructors by doing this: MyType({}, <params>);
.
To avoid this, you will need to give Key
an additional (private) constructor or otherwise prevent it from being an aggregate.