2017-01-30 2 views
3

Soweit ich [class.copy.ctor] und [class.copy.assign], struct A im folgenden Code verstehen sollte nicht bewegen-konstruierbar noch bewegen zuweisbare:Unmögliche implizite Verschiebeoperationen?

#include <type_traits> 


struct X { 
    X() noexcept; // user-declared default constructor 
    ~X() noexcept; // Force X not to be trivially copyable 
    X(X &&) = delete; // Explicitly deleted move constructor 
    X(X const &) = delete; // Explicitly deleted copy constructor 
    X & operator=(X &&) = delete; // Explicitly deleted move assignment operator 
    X & operator=(X const &) = delete; // Explicitly deleted copy assignment op. 
}; 
static_assert(!std::is_copy_constructible<X>::value, ""); 
static_assert(!std::is_copy_assignable<X>::value, ""); 
static_assert(!std::is_move_assignable<X>::value, ""); 
static_assert(!std::is_move_constructible<X>::value, ""); 
static_assert(!std::is_trivially_copyable<X>::value, ""); 
static_assert(!std::is_trivially_copy_assignable<X>::value, ""); 
static_assert(!std::is_trivially_copy_constructible<X>::value, ""); 
static_assert(!std::is_trivially_move_assignable<X>::value, ""); 
static_assert(!std::is_trivially_move_constructible<X>::value, ""); 


struct A { 
    A() noexcept; // user-declared default constructor 
    A(A const &) noexcept; // user-declared copy constructor 
    A & operator=(A const &) noexcept; // user-declared copy assignment operator 
    X x; 
}; 
static_assert(std::is_copy_constructible<A>::value, ""); 
static_assert(std::is_copy_assignable<A>::value, ""); 
static_assert(!std::is_move_assignable<A>::value, "");  // FAILS?! 
static_assert(!std::is_move_constructible<A>::value, "");  // FAILS?! 
static_assert(!std::is_trivially_copyable<A>::value, ""); 
static_assert(!std::is_trivially_copy_assignable<A>::value, ""); 
static_assert(!std::is_trivially_copy_constructible<A>::value, ""); 
static_assert(!std::is_trivially_move_assignable<A>::value, ""); 
static_assert(!std::is_trivially_move_constructible<A>::value, ""); 

jedoch zwei der statischen Behauptungen scheitern sowohl mit GCC und Clang, die bedeutet, dass aus irgendeinem Grund A move-assignable und move-constructible ist.

In meiner Argumentation sollte dies nicht sein, denn struct A:

  • nicht explizit einen Umzug Konstruktor
  • erklären auch keine Aussage explizit einen Zug Zuweisungsoperator
  • ein benutzer erklärt Copykonstruktor hat
  • verfügt über einen vom Benutzer deklarierten Kopierzuweisungsoperator.
  • hat ein Feld x vom Typ X, das nicht mit anderen A::x direkt initialisiert werden kann, da alle Konstruktoren von X, die für die Überladungsauflösung teilnehmen würden, explizit gelöscht werden.

Ist das ein Compiler Bug oder verkenne ich etwas?

Antwort

6

Sie sind Verhaltensweisen zu erwarten, weil die Existenz von Copykonstruktor und kopieren Zuweisungsoperator die Anforderung von MoveConstructible

Eine Klasse erfüllt nicht eine Bewegung Konstruktor implementieren diese Art Anforderung gerecht zu werden: eine Kopie Konstruktor das dauert ein const T & Argument kann rvalue Ausdrücke binden.

und MoveAssignable.

Der Typ muss nicht bewegen Zuweisungsoperator implementieren, um diese Art Anforderung gerecht zu werden: eine Kopie Zuweisungsoperator, der seine Parameter als Wert oder als const Typ &, binden an rvalue Argument.

Beachten Sie, dass std::is_move_constructible und std::is_move_assignable sind nur ein Objekt des angegebenen Typs Überprüfung kann aus einem R-Wert Argument aufgebaut/zugewiesen werden. Selbst wenn es keinen Verschiebungskonstruktor/Zuweisungsoperator gibt, könnte der Kopierkonstruktor/Zuweisungsoperator die Arbeit ausführen, da ein Rvalue-Argument auch an eine Lvalue-Referenz an const übergeben werden könnte.

EDIT

Hinweis für den Beispielcode Sie den Umzug Konstruktor/Zuweisungsoperator zeigte nicht implcitly überhaupt erklärt (wegen der Existenz von benutzer erklärt Copykonstruktor und kopieren Zuweisungsoperator), so dass sie gewonnen Auswirkungen auf die Überladungsauflösung und das Ergebnis, dass der Kopierkonstruktor/Zuweisungsoperator aufgerufen wird. Aber wenn Sie sie explizit als delete deklariert haben, wird sich das Verhalten ändern, da explizit gelöschte Funktionen an der Überladungsauflösung teilnehmen, und sie werden bevorzugt ausgewählt. Dann geben std::is_move_constructible und std::is_move_assignablefalse zurück.

2

is_move_constructible/assignable überprüft nicht, ob es Move Constructor oder Move Assignment Operator gibt. Es überprüft, ob die Klasse aus der r-Wert-Referenz konstruiert/zugewiesen werden kann. Klassen ohne Bewegungseigenschaften würden in diesem Fall einfach den Kopierkonstruktor/Zuweisungsoperator verwenden.

Im Allgemeinen is_move_constructible/assignable ist schwächer als is_copy_constructible/assignable: später scheitert auf Nur-Bewegung-Typen, wenn ehemalige Pässe überprüfen.

+1

@jotik du hast sie nicht von 'A' gelöscht. Gelöscht und nicht generiert sind verschiedene Dinge: Gelöschte Funktionen/Konstruktoren nehmen an der Überladungsauflösung teil, nicht generierte Funktionen nicht. Außerdem wird 'x' member standardmäßig überall initialisiert, da Sie es in benutzerdefinierten Konstruktoren nicht anders angegeben haben. –