2014-04-23 18 views
9

Sto usando GCC 4.8 per compilare il codice seguente:Perché ricevo un errore di compilazione?

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

Qualcuno mi può spiegare il motivo di questo errore del compilatore?

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

              ^

risposta

11

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

Il costruttore che prende int è dichiarato per essere private che è perché è dando errore di compilazione. Si noti che il costruttore viene richiamato da make_unique (che non ha accesso ai membri privati), non da create.

Tuttavia, probabilmente ti starai chiedendo perché la prima chiamata a create() compila bene, penso che sia perché GCC ha il bug. Non dovrebbe essere compilato neanche in questo caso, perché il costruttore predefinito è dichiarato essere private. Clang restituisce correttamente l'errore per entrambe le chiamate (see this).

In ogni caso, se si desidera tenerli private, quindi rendere make_unique un amico della classe.

2

Nella struttura C++, tutti i membri sono pubblici per impostazione predefinita. In una dichiarazione di classe, i membri sono privati ​​per impostazione predefinita. Nel tuo caso, i costruttori sono stati fatti privati, è per questo che si ottiene l'errore: S :: S (int) è stato privato

Quindi, apportare le modifiche come:

public: 
    S(int) {} 
    S() = default; 
+0

Non voglio rendere pubblici i costruttori. Questo è il motivo per cui ho introdotto create(), Inoltre, per alcuni motivi il costruttore privato S() non fa fallire la compilazione quando è privata, mentre S (int) fa fallire la compilazione ... – Martin

1

Se qualche costruttore è privato, significa che nessuno, tranne la classe stessa (e gli amici), dovrebbe essere in grado di crearne delle istanze usando quel costruttore.

Per creare un'istanza di una classe che ha solo costruttori privati, è necessario utilizzare un metodo statico.

+1

Che è esattamente ciò che è già facendo ... –

4

La ragione è abbastanza semplice:

Il costruttore non viene chiamato dall'interno S::create, ma dall'interno del modello ::make_unique funzione, che non ha accesso alla funzione di membro privato S::S(int).

Una soluzione semplice sarebbe chiamare da soli new (vedere here).

In realtà, la questione più interessante è il motivo per cui non lo fa l'errore alla prima chiamata così ...

+5

Penso che il problema dell'OP sia che (abbastanza ragionevolmente!) Vuole evitare di chiamare 'new' esplicitamente nel codice. –

2

Se si desidera mantenere i costruttori della classe privata, è necessario effettuare qualsiasi utente non membro (qui: make_unique) ad un amico:

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

alternativa, si può evitare di make_unique<> e creare direttamente il unique_ptr<S> da un membro statico:

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