2014-04-23 24 views
9

Ich bin mit 4.8 GCC den folgenden Code zu kompilieren:Warum erhalte ich einen Kompilierungsfehler?

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

Kann mir jemand erklären, den Grund dieses Fehlers vom 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)...});

              ^

Antwort

11

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

Der Konstruktor die dauert int wird private erklärt, weshalb es einen Kompilierungsfehler gibt. Beachten Sie, dass der Konstruktor von make_unique (das keinen Zugriff auf private Mitglieder hat) aufgerufen wird, nicht von create.

Allerdings sind Sie wahrscheinlich fragen, warum der erste Aufruf zu create() kompiliert gut, ich denke, es liegt daran, dass GCC Bug hat. Es sollte auch in diesem Fall nicht kompiliert werden, da der Standardkonstruktor auch als private deklariert wird. Clang gibt für beide Aufrufe korrekt (see this).

Wie auch immer, wenn Sie sie private behalten möchten, dann machen Sie make_unique einen Freund der Klasse.

2

In C++ - Struktur sind alle Mitglieder standardmäßig öffentlich. In einer Klassendeklaration sind Mitglieder standardmäßig privat. In Ihrem Fall haben Konstrukteure gemacht privat, das ist, warum Sie den Fehler: S :: S (int) wurde der private

Also, machen Sie die Änderungen wie:

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

Ich möchte die Konstrukteure nicht veröffentlichen. Deshalb habe ich create() eingeführt. Desweiteren macht der private S() - Konstruktor aus irgendwelchen Gründen die Kompilierung nicht fehlgeschlagen, wenn sie privat ist, während S (int) die Kompilierung fehlschlägt ... – Martin

1

Wenn ein Konstruktor privat ist, bedeutet dies, dass niemand außer der Klasse selbst (und Freunden) in der Lage sein sollte, Instanzen davon unter Verwendung dieses Konstruktors zu erstellen.

Um eine Instanz einer Klasse zu erstellen, die nur Konstruktoren von privates hat, müssen Sie eine statische Methode verwenden.

+1

Genau das ist er schon tun... –

4

Der Grund ist ziemlich einfach:

Der Konstruktor nicht aus S::create genannt wird, aber innerhalb der ::make_unique Funktionsvorlage, die keinen Zugriff auf die privaten Member-Funktion S::S(int) hat.

Eine einfache Lösung wäre, new selbst aufzurufen (siehe here).

Eigentlich ist die interessantere Frage ist, warum es beim ersten Anruf auch nicht Fehler macht ...

+5

Ich denke, das Problem von OP ist, dass er (ziemlich vernünftig!) Vermeiden will, "neu" explizit im Code zu nennen. –

2

Wenn Sie die Klasse des Konstrukteurs privat halten möchten, können Sie eine beliebige Nicht-Mitglied Benutzer (hier machen muss: make_unique) ein Freund:

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

Alternativ können Sie make_unique<> vermeiden und direkt die unique_ptr<S> von einem statischen Element erstellen:

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