C++ Singleton private constructor not accessible from static function

I have a singleton class declaration here:

#ifndef GLFW_CONTEXT_H
#define GLFW_CONTEXT_H

#include <memory>

class GLFWContextSingleton
{
public:
    static std::shared_ptr<GLFWContextSingleton> GetInstance();
    ~GLFWContextSingleton();
    GLFWContextSingleton(const GLFWContextSingleton& other) = delete;
    GLFWContextSingleton* operator=(const GLFWContextSingleton* other) = delete;
    
private:
    GLFWContextSingleton();
};

#endif

and an implementation of the GetInstance function shown here

std::shared_ptr<GLFWContextSingleton> GLFWContextSingleton::GetInstance()
{
    static std::weak_ptr<GLFWContextSingleton> weak_singleton_instance;
    auto singleton_instance = weak_singleton_instance.lock();

    if (singleton_instance == nullptr)
    {
        singleton_instance = std::make_shared<GLFWContextSingleton>();
        weak_singleton_instance = singleton_instance;
    }

    return singleton_instance;
}

However the call to std::make_shared<GLFWContextSingleton>() gives me an error saying

‘GLFWContextSingleton::GLFWContextSingleton()’ is private within this context

I thought that this static method would have access to the private member functions. What is causing this and how do I fix it?

Answer

The static function does have access to private members. make_shared does not.

make_shared is a template function that forwards the arguments it gets and calls the constructor of the specified class. So the call to the default constructor happens inside the make_shared function, not inside the GetInstance function, hence the error.

One way to deal with this is to use a private nested class as the only argument to the constructor.

#include <memory>

class GLFWContextSingleton
{
private:
    struct PrivateTag {};
public:
    static std::shared_ptr<GLFWContextSingleton> GetInstance();
    ~GLFWContextSingleton();
    GLFWContextSingleton(const GLFWContextSingleton& other) = delete;
    GLFWContextSingleton* operator=(const GLFWContextSingleton* other) = delete;
    
    GLFWContextSingleton(PrivateTag);
};

std::shared_ptr<GLFWContextSingleton> GLFWContextSingleton::GetInstance()
{
    static std::weak_ptr<GLFWContextSingleton> weak_singleton_instance;
    auto singleton_instance = weak_singleton_instance.lock();

    if (singleton_instance == nullptr)
    {
        singleton_instance = std::make_shared<GLFWContextSingleton>(PrivateTag{});
        weak_singleton_instance = singleton_instance;
    }

    return singleton_instance;
}

int main() {

}

This way we keep the constructor public, but in order to use it we need a PrivateTag, only accessible to members of the class.