Martin
Martin

Reputation: 9359

Why do I get a compilation error?

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)...});

                                                             ^

Upvotes: 9

Views: 1113

Answers (5)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361322

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.

Upvotes: 12

Walter
Walter

Reputation: 45414

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
};

Upvotes: 2

danielschemmel
danielschemmel

Reputation: 11116

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...

Upvotes: 4

jhamon
jhamon

Reputation: 3691

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.

Upvotes: 1

Dr. Debasish Jana
Dr. Debasish Jana

Reputation: 7118

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;

Upvotes: 2

Related Questions