3

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?

7
  • The function std::make_shared<GLFWContextSingleton>() is not a friend of your class. It can't use the private constructor.
    – Ted Lyngmo
    Commented Oct 20, 2021 at 20:42
  • 1
    Yes, the static function has access. You can do singleton_instance.reset(new GLFWContextSingleton); - it's the function you call, make_shared, that doesn't have access.
    – Ted Lyngmo
    Commented Oct 20, 2021 at 20:46
  • 2
    Any reason why you want a shared_ptr to the singleton btw? Wouldn't a Mayers-kind-of-singleton work?
    – Ted Lyngmo
    Commented Oct 20, 2021 at 20:55
  • 2
    @TedLyngmo I thought it might be better to use a shared_ptr because I can use a weak_ptr to test if the object has been initialized or not. But now that you mention it, I don't see a need for it and probably can just return a reference to a bare static object itself.
    – nick2225
    Commented Oct 20, 2021 at 20:57
  • 1
    Not to mention, the way GetInstance() is shown, there is a race condition if multiple threads were to call GetInstance() at the same time. Returning a static object would avoid that. Commented Oct 20, 2021 at 21:16

1 Answer 1

6

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.

1
  • Thanks! I completely forgot that access permissions for the class were only available at the immediate level of the static function and not propagated down to other calls.
    – nick2225
    Commented Oct 20, 2021 at 20:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.