shared_instance implementation using C++ and RAII
Non-Default Constructors:

Adding support for non-default constructible objects in a "lazy" fashion, could be done by adding a factory function of std::function&lt;std::shared_ptr&lt; t_type &gt;()&gt;.

template&lt; typename t_type &gt;
struct shared_instance
{
    using t_factory = std::function&lt;std::shared_ptr&lt; t_type &gt;()&gt;;

    static t_factory factory; // MUST be set before calling get_shared_instance

    static std::shared_ptr&lt; t_type &gt; get_shared_instance()
    {
        ...


            assert(factory); // or throw, or return empty shared_ptr

            result = factory(); // instead of make_shared

        ...
    }

};

// and define the static member...
template&lt; typename t_type &gt;
typename shared_instance&lt; t_type &gt;::t_factory shared_instance&lt; t_type &gt;::factory;


...

int main()
{
    context&lt;int&gt; c; // not ok... haven't set factory yet!

    // do this before any call to get_shared_instance
    shared_instance&lt;int&gt;::factory = [] () { return std::make_shared&lt;int&gt;(5); }; // store things in the lambda by value, not reference...

    context&lt;int&gt; c; // ok
}


I'm not sure this counts as elegant though, as it gives you a two-step initialization process. This could be fixed by recognising the shared_instance itself as a kind of factory, making its contents non-static, and passing it into the contexts:

template&lt; typename t &gt;
struct context
{
    context(instance_provider&lt; t &gt;&amp; provider): // now it can't be created unless this exists...
        instance{ provider.get_shared_instance() }
    { }

private:

    std::shared_ptr&lt; t &gt; instance;
};

int main()
{
    auto provider = instance_provider&lt;int&gt;([] () { return std::make_shared&lt;int&gt;(5); });

    context&lt;int&gt; c(provider);
}


Further Considerations:

Does the C library you're using say anything about context lifetime itself? (e.g. Winsock reference counts calls to WSAStartup() and WSACleanup() internally, so as long as the calls are paired properly the C library handles cleaning up)

The main feature that this shared_instance class seems to provide is destroying The Instance promptly after the last context is destroyed. RAII itself could be achieved by just storing a unique pointer over the maximum possible lifetime of any contexts, which would be simpler. Is this prompt destruction actually important?

If not, then it's just a singleton, which we either live with, or do the work to pass a pointer or reference to the thing you need where it needs to be.