Skip to content

Instantly share code, notes, and snippets.

@SeverTopan
Last active November 7, 2018 07:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SeverTopan/daa7cbb2ebbb03610717435c42d9a794 to your computer and use it in GitHub Desktop.
Save SeverTopan/daa7cbb2ebbb03610717435c42d9a794 to your computer and use it in GitHub Desktop.
Casting Away the Private Constructor std::make_shared Boilerplate

A common problem that I have come across exists when attempting to create shared pointers of objects which have protected constructors. The following class design details a class that can only be accessed through a shared reference.

class A
{
protected:
    A(int foo)
        : _foo(foo)
    {
    }

public:
    static std::shared_ptr<A> Create(int foo)
    {
        return std::shared_ptr<A>(new A(foo));
    }

    int _foo;
};

However, there is a problem with this implementation. The STL's smart pointers point to the usage of std::make_shared and std::make_unique for the instantiation of smart pointers, since these function calls perform one heap allocation instead of two when contrasted with std::shared_ptr<A>(new A()).

However this opens a can of worms, since the std::make_* function calls cannot access private or protected constructors of an object they are instantiating, which is a desired property in certain designs. So simply substituting the std::shared_ptr<A>(new A()) call above with a std::make_shared will not compile. One solution, offered by this post is delineated below.

class A
{
protected:
    A(int foo)
        : _foo(foo)
    {
    }

public:
    static std::shared_ptr<A> CreateWithBoilerplate(int foo)
    {
        struct BoilerplateCreator : public A
        {
            BoilerplateCreator(int foo)
                : A(foo)
                {
                }
        };

        return std::make_shared<BoilerplateCreator>(foo);
    }

    int _foo;
};

As you can see, the boiler-plate present in this solution is substantial, and grows with the number of arguments in the function signiature of A.

I would like to propose a general solution to this design paradigm:

template <typename T>
class Creator : public T
{
public:
    template <typename... Args>
    Creator(Args... args)
        : T(std::move(args)...)
    {
    }
};

class A
{
protected:
    A(int foo)
        : _foo(foo)
    {
    }

public:
    static std::shared_ptr<A> CreateWithoutBoilerPlate(int foo)
    {
        return std::make_shared<Creator<A>>(foo);
    }

    int _foo;
};

Here we essentially provide a solution to elide the privacy of the constructor of the class used within the Creator template. This solution inspired by the curiously recurring template pattern. Inheritance from a template argument is used to remove the need for a new class definition with each definition of a creator method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment