Enabling make_unique with Private Constructors

A problem I've frequently encountered is the desire to use unique_ptr or another smart pointer with a class that only has private constructors.

An example would be a class that requires a non-trivial factory procedure that isn't appropriate to put in a constructor itself. Component factories in some game engine designs, for instance.

The issue is that make_unique must be able to invoke a constructor (and operator new). Calling make_unique from within a static member function does not bestow the maker function with the access privileges of the callee, naturally. This has in the past led me to just leave constructors public with some notes that it should not be used by others. That's a little unsatisfying.

Thankfully, a number of clever folks have come up with workarounds. My particular favorite at the moment is the one outlined at http://rienajouter.blogspot.com/2014/10/makeshared-and-makeunique-for-classes.html.

The short version: make all the constructors public but have them require a non-public tag type as a parameter. External users cannot construct the tag type and hence cannot invoke the constructors. The static member functions can construct the tag classes and then forwards them through make_unique so construction works correctly.

Example:

class Test  
{
  struct _constructor_tag { explicit _constructor_tag() = default; };

public:  
  Test(_constructor_tag) {}

  static unique_ptr<Test> factory()
  {
    return make_unique<Test>(_constructor_tag{});
  }
};

void test()  
{
  auto t1 = Test::factory(); // GOOD
  auto t2 = make_unique<Test>(); // ERROR
  auto t3 = make_unique<Test>()(Test::_constructor_tag); // ERROR
}

There's a few other variants of the above. If the class naturally uses some internal implementation type, like a PIMPL-style Impl inner class for example, there's the potential of relying on that type instead of an additional tag type.

Another variant is described at http://stackoverflow.com/a/25069711/720860. It uses an inheritance trick to avoid the need to specially declare tag types or constructor overloads. It's fairly clever, but I'm not a fan of abusing inheritance. It can be particularly obnoxious if the type has virtual members, since the trick may generate an additional vtable and RTTI information. The primary advantage of the trick is that all the changes to support maker functions are localized to the factories themselves.

Example of the second trick:

class Test  
{
public:  
  static unique_ptr<Test> factory()
  {
    struct EnableMaker : public Test { using Test::Test; };
    return make_unique<EnableMaker>();
  }

private:  
  Test() = default;
};

Either of those general approaches work. I have my favorite, but it's safe to use whichever seems best for a given project's style or needs.

Sean Middleditch

Read more posts by this author.