2017-03-01 1 views
2

Mechanismus dafür ist hier gut erklärt: Template "copy constructor" does not prevent compiler-generated move constructor, aber ich möchte besser verstehen, warum es so gemacht wird. Ich verstehe, dass der move -Konstruktor nicht generiert wird, selbst wenn ein anderer Konstruktor vom Programmierer geschrieben wird, da dies ein Hinweis darauf ist, dass die Konstruktion eines Objekts nicht trivial ist und der automatisch generierte Konstruktor wahrscheinlich falsch ist. Warum werden Template-Konstruktoren, die die gleiche Signatur wie Kopierkonstruktoren haben, nicht einfach Kopierkonstruktoren genannt?Warum generieren Compiler einen Kopier-/Verschiebungskonstruktor, wenn es einen Vorlagenkonstruktor gibt?

Beispiel:

class Person { 
public: 
    template<typename T> 
    Person(T&& t) : s(std::forward<T>(t)) { 
     std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 

    Person(int n) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 

    // No need to declare copy/move constructors as compiler will do this implicitly 
    // Templated constructor does not inhibit it. 
    //Person(const Person&) = default; 
    //Person(Person&&) = default; 

private: 
    std::string s; 
}; 

und dann:

Person p("asd");  // OK! 
//Person p4(p);   // error as Person(T&&) is a better match 

wenn ich machen p konst:

const Person p("asd"); 
Person p4(p);   // thats ok, generator constructor is a better match 

aber wenn ich löschen explizit auch einen Umzug Konstruktor mit:

Person(Person&&) = delete; 

dann ist die automatische Generierung von Konstruktoren gesperrt.

+0

"* Ich verstehe, dass Move-Konstruktor nicht generiert wird, auch wenn ein anderer Konstruktor vom Programmierer geschrieben wird *" Das ist nicht wahr. Nur das Vorhandensein von Kopierkonstruktoren/-zuweisungen verhindert die Generierung von Bewegungskonstruktoren. –

+0

@NicolBolas danke, ich nachgeschlagenen Artikel 17 in Effective Modern C++, und auch bewegen Konstruktor/Zuweisung Operator und Destruktor verhindert Generierung von Move Konstruktor oder Zuweisungsoperator. – mike

Antwort

4

Sie verstehen falsch.

struct noisy { 
    noisy() { std::cout << "ctor()\n"; } 
    noisy(noisy&&) { std::cout << "ctor(&&)\n"; } 
    noisy(noisy const&) { std::cout << "ctor(const&)\n"; } 
    noisy& operator=(noisy&&) { std::cout << "asgn(&&)\n"; return *this; } 
    noisy& operator=(noisy const&) { std::cout << "asgn(const&)\n"; return *this; } 
}; 

struct test { 
    noisy n; 
    test(int x) { (void)x; } 
}; 

test hat Verschieben/Kopieren Konstrukt/Zuordnung erzeugt wird.

Live example.

Ein von einem Programmierer geschriebenes Kopieren/Verschieben-Konstrukt/Zuweisung führt dazu, dass die anderen unterdrückt werden.

Jetzt unterdrückt das Schreiben eines Konstruktors den Zero-Argument-Konstruktor. Vielleicht bist du deshalb verwirrt.

Vorlagen mit Templaten mit der gleichen Signatur wie Kopierkonstruktoren sind keine Kopierkonstruktoren, da der Standard dies besagt.

Wie es passiert, ist Vorlagencode selten der richtige Code für eine Kopie oder einen Move-Konstruktor/eine Zuordnung.

Die Tatsache, dass die Weiterleitung Referenzen häufig self& und self const&& kopieren/verschieben über die tatsächlichen Kopieren/Verschieben-Vorgänge ist ein Problem. C++ ist nicht perfekt.

Normalerweise ist die Art und Weise, dies zu vermeiden ist:

template<class T, 
    class=std::enable_if_t< 
    !std::is_same<std::decay_t<T>, Person>::value 
    > 
> 
Person(T&& t) : s(std::forward<T>(t)) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
} 

oder !std::is_base_of<Person, std::decay_t<T>>::value, die einige andere Situationen (wie Vererbungsbauer) abdeckt.

+0

Vielen Dank, ich lese jetzt Artikel 27 von Effective Modern C++, die Möglichkeiten zur Vermeidung von Problemen mit universellen (oder jetzt weiterleitenden) Referenzkonstruktoren behandelt. Schade, Scott Meyers geht nicht tief in SFINAE. – mike

Verwandte Themen