9
votes

I am using GCC 4.8 to compile the following code:

#include <memory>

template<typename T, typename ...Args>
    std::unique_ptr<T> make_unique(Args&& ...args) {
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

struct S {
    template<class... Args>
    static std::unique_ptr<S> create(Args&&... args) {
        return make_unique<S>(std::forward<Args>(args)...);
    }
private: // if I remove this line, then the compilation is OK
    S(int) {}
    S() = default;
};

int main() {
    auto s1 = S::create(); // OK
    auto s2 = S::create(0); // Compilation error
}

Can anyone explain me the reason of this error from the compiler?

main.cpp: In instantiation of 'std::unique_ptr make_unique(Args&& ...) [with T = S; Args = {int}]':

main.cpp:11:58: required from 'static std::unique_ptr S::create(Args&& ...) [with Args = {int}]'

main.cpp:20:26: required from here

main.cpp:14:5: error: 'S::S(int)' is private

 S(int) {}
 ^

main.cpp:5:65: error: within this context return std::unique_ptr(new T{std::forward(args)...});

                                                             ^
5

5 Answers

11
votes

Can anyone explain me the reason of this error from the compiler?

The constructor which takes int is declared to be private which is why it is giving compilation error. Note that the constructor is getting invoked from make_unique (which doesn't have access to private members), not from create.

However, you're probably wondering why the first call to to create() compiles fine, I think it is because GCC has bug. It should not compile even in this case, because the default constructor is declared to be private as well. Clang correctly gives error for both calls (see this).

Anyway, if you want to keep them private, then make make_unique a friend of the class.

4
votes

The reason is fairly simple:

The constructor is not called from within S::create, but from within the ::make_unique function template, which does not have access to the private member function S::S(int).

A simple fix would be to call new yourself (see here).

Actually, the more interesting question is why it does not error on the first call as well...

2
votes

In C++ struct, all members are public by default. In a class declaration, members are private by default. In your case, constructors have been made private, that's why you get the error: S::S(int) was private

So, make the changes as:

public: 
    S(int) {}
    S() = default;
2
votes

If you want to keep the class's constructors private, you must make any non-member user (here: make_unique) a friend:

struct S {
  template<typename T, typename ...Args>
  friend std::unique_ptr<T> make_unique(Args&& ...args);
  // rest as before
};

Alternatively, you may avoid make_unique<> and directly create the unique_ptr<S> from a static member:

struct S {
  template<class... Args>
  static std::unique_ptr<S> create(Args&&... args)
  { return std::unique_ptr<S>(new S{std::forward<Args>(args)...}); }
  // rest as before
};
1
votes

If some constructor is private, it means that no one but the class itself (and friends) should be able to create instances of it using that constructor.

To create instance of a class which has only privates constructors, you have to use a static method.