I am trying to apply the builder pattern to an object, but the private constructor is not visible from the inner class.
#include <iostream>
#include <memory>
class Outer{
private:
Outer(void){ std::cout << "Constructed!" << std::endl; }
public:
class Builder{
public:
std::unique_ptr<Outer> build(void){
return std::make_unique<Outer>();
}
};
};
int main(int argc, char** agrs){
std::unique_ptr<Outer> instance = Outer::Builder().build();
return 0;
}
fails with the following error:
In file included from /usr/include/c++/8/memory:80,
from scrap.cpp:2:
/usr/include/c++/8/bits/unique_ptr.h: In instantiation of ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = Outer; _Args = {}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<Outer>]’:
scrap.cpp:11:35: required from here
/usr/include/c++/8/bits/unique_ptr.h:831:30: error: ‘Outer::Outer()’ is private within this context
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
scrap.cpp:6:2: note: declared private here
Outer(void){ std::cout << "Constructed!" << std::endl; }
^~~~~
I tried friend class Outer::Builder
in the definition, but Outer
is incomplete at the friend clause, so I couldn't make it work with that. I would very much like to restrict instantiation of the object to the Builder
class, is there any way to do that in C++?
Or is making the Outer
constructor public the only option?
This doesn't work because the real builder is std::make_unique
, and it is neither a friend nor a member. Making it a friend is not really possible, because you don't know what internal function it delegates to, and it would defeat the purpose of a private constructor anyway.
You can just use bare new
instead of std::make_unique
, it will work in a pinch. If instead of a unique pointer you want a shared pointer, this becomes a bit more problematic, since the performance will not be as good.
Here's how to make it work for unique_ptr
, shared_ptr
or any other kind of handle.
#include <memory>
class Outer
{
private:
Outer();
public:
class Builder
{
private:
class InnerOuter;
public:
std::unique_ptr<Outer> build();
};
};
class Outer::Builder::InnerOuter : public Outer
{
public:
using Outer::Outer;
};
std::unique_ptr<Outer> Outer::Builder::build()
{
return std::make_unique<InnerOuter>();
}
Now only Outer::Builder
can refer to (and construct) an InnerOuter
, because it is a private class. But its constructor is public, so std::make_unique
can access it.
Note, InnerOuter
can access a private constructor of Outer
because it is a member of a member of Outer
and have member access to Outer
.