Warum wird dieser Code in C ++ 17 fehlerfrei kompiliert? [Duplikat]

Sep 29 2020

Ich habe alle Konstruktoren gelöscht, auch dann funktioniert der folgende Code einwandfrei. Wie und warum ?

class Ax
{    
    public:
    
    Ax() = delete;
    Ax(Ax const&)=delete;
    Ax(Ax&&)=delete;
    void operator=(Ax const&)=delete;
    void operator=(Ax&&)=delete;

    void print()
    {
        cout << "Hello \n";
    }
};

int main(int argc, char** argv) 
{           
    Ax{}.print();
    return 0;
}

Antworten

43 dfrib Sep 29 2020 at 07:37

(Eine ausführliche Beschreibung dieses Themas finden Sie im Blog-Artikel Das launische Aggregat. )


Aggregierte Initialisierung

Klasse Axist ein Aggregat in C ++ 11, C ++ 14 und C ++ 17, da es keine vom Benutzer bereitgestellten Konstruktoren enthält. Dies bedeutet, dass Ax{}die Aggregatinitialisierung alle vom Benutzer deklarierten Konstruktoren umgeht , auch gelöschte.

struct NonConstructible {
    NonConstructible() = delete;
    NonConstructible(const NonConstructible&) = delete;
    NonConstructible(NonConstructible&&) = delete;
};

int main() {
    //NonConstructible nc;  // error: call to deleted constructor

    // Aggregate initialization (and thus accepted) in
    // C++11, C++14 and C++17.
    // Rejected in C++20 (error: call to deleted constructor).
    NonConstructible nc{};
}

Die Definition einer Aggregatklasse hat sich durch verschiedene Standardversionen (C ++ 11 bis C ++ 20) geändert, und diese Regeln können etwas überraschende Konsequenzen haben. Ab C ++ 20, insbesondere aufgrund der Implementierung von

  • P1008R1: Aggregate mit vom Benutzer deklarierten Konstruktoren verbieten

Das meiste der häufig überraschenden Aggregatverhalten wurde behoben, indem insbesondere nicht mehr zugelassen wurde, dass Aggregate vom Benutzer deklarierte Konstruktoren haben. Dies ist eine strengere Anforderung, dass eine Klasse ein Aggregat sein muss, als nur vom Benutzer bereitgestellte Konstruktoren zu verbieten .


Vom Benutzer bereitgestellte oder nur vom Benutzer deklarierte explizit voreingestellte Konstruktoren

Beachten Sie, dass das Bereitstellen einer explizit voreingestellten (oder gelöschten) Definition außerhalb der Zeile als vom Benutzer bereitgestellter Konstruktor gilt. Dies bedeutet, dass im folgenden Beispiel Bein vom Benutzer angegebener Standardkonstruktor vorhanden ist, während Adies nicht der Fall ist:

struct A {
    A() = default; // not user-provided.
    int a;
};

struct B {
    B(); // user-provided.
    int b;
};

// Out of line definition: a user-provided
// explicitly-defaulted constructor.
B::B() = default;

mit dem Ergebnis, dass Aein Aggregat ist, während dies Bnicht der Fall ist. Dies bedeutet wiederum, dass die Initialisierung Bmittels eines leeren Direktlisten-Init dazu führt, dass sein Datenelement bin einem nicht initialisierten Zustand belassen wird. Für Ajedoch wird die gleiche Initialisierung Syntax Ergebnis in (über aggregierte Initialisierung des AObjekts und nachfolgenden Wert initialisiert sein Datenelement a) Null-Initialisierung seines Datenelementes a:

A a{};
// Empty brace direct-list-init:
// -> A has no user-provided constructor
// -> aggregate initialization
// -> data member 'a' is value-initialized
// -> data member 'a' is zero-initialized

B b{};
// Empty brace direct-list-init:
// -> B has a user-provided constructor
// -> value-initialization
// -> default-initialization
// -> the explicitly-defaulted constructor will
//    not initialize the data member 'b'
// -> data member 'b' is left in an unititialized state

Dies kann überraschend sein und mit dem offensichtlichen Risiko verbunden sein, das nicht initialisierte Datenelement bmit dem Ergebnis eines undefinierten Verhaltens zu lesen :

A a{};
B b{};     // may appear as a sound and complete initialization of 'b'.
a.a = b.b; // reading uninitialized 'b.b': undefined behaviour.
12 N.Shead Sep 29 2020 at 07:36

In C ++ 17 ist Ihr Beispiel ein Aggregat. Für C ++ 17-Aggregate müssen nur keine vom Benutzer bereitgestellten Konstruktoren vorhanden sein. Vom Benutzer deklarierte (aber explizit gelöschte oder voreingestellte) Konstruktoren sind in Ordnung.

In diesem Fall wird dann eine aggregierte Initialisierung durchgeführt Ax{}, bei der keiner der Konstruktoren aufgerufen wird ... einschließlich der gelöschten, und dies wird kompiliert.

In C ++ 20 wurden die Regeln so geändert, dass alle vom Benutzer deklarierten Konstruktoren verhindern, dass der Typ ein Aggregat ist, und das Beispiel daher nicht kompiliert werden kann.

Siehe auch https://en.cppreference.com/w/cpp/language/aggregate_initialization