Ausgewiesene Initialisierer in C ++ 20

25

Ich habe eine Frage zu einer der C ++ 20-Funktionen, die als Initialisierer bezeichnet werden (weitere Informationen zu dieser Funktion hier ).

#include <iostream>

constexpr unsigned DEFAULT_SALARY {10000};

struct Person
{
    std::string name{};
    std::string surname{};
    unsigned age{};
};

struct Employee : Person
{
    unsigned salary{DEFAULT_SALARY};
};

int main()
{
    std::cout << std::boolalpha << std::is_aggregate_v<Person> << '\n'; // true is printed
    std::cout << std::boolalpha << std::is_aggregate_v<Employee> << '\n'; // true is printed

    Person p{.name{"John"}, .surname{"Wick"}, .age{40}}; // it's ok
    Employee e1{.name{"John"}, .surname{"Wick"}, .age{40}, .salary{50000}}; // doesn't compile, WHY ?

    // For e2 compiler prints a warning "missing initializer for member 'Employee::<anonymous>' [-Wmissing-field-initializers]"
    Employee e2 {.salary{55000}}; 
}

Dieser Code wurde mit gcc 9.2.0 und -Wall -Wextra -std=gnu++2aFlags kompiliert .

Wie Sie oben sehen können, sind beide Strukturen Personund EmployeeAggregate, aber die Initialisierung des EmployeeAggregats ist mit bestimmten Initialisierern nicht möglich.

Könnte mir jemand erklären warum?

MateuszGierczak
quelle
Ich weiß nicht, ob es Ihr Problem löst, aber Sie können hier nicht öffentlich erben ...struct Employee : public Person
skratchi.at
@ skratchi.at stackoverflow.com/a/3965003/11683
GSerg
@ GSerg Ok, na ja ... ich habe nie darüber nachgedacht, da ich es benutze publicoder privatejedes Mal ... trotzdem danke
skratchi.at
Was ist dein genauer Fehler, den du bekommst?
skratchi.at
2
@ skratchi.at structs erben standardmäßig öffentlich
idclev 463035818

Antworten:

15

Gemäß dem C ++ 20-Standard (9.3.1 Aggregate. S. # 3)

(3.1) - Wenn die Initialisierungsliste eine designierte Initialisiererliste ist, muss das Aggregat vom Klassentyp sein, der Bezeichner in jedem Bezeichner muss ein direktes nicht statisches Datenelement der Klasse und die explizit initialisierten Elemente des Aggregats benennen sind die Elemente, die diese Mitglieder sind oder enthalten.

Daher dürfen Sie die angegebene Initialisierungsliste nicht zum Initialisieren von Datenelementen von Basisklassen verwenden.

Verwenden Sie stattdessen die übliche Listeninitialisierung wie

Employee e1{ "John", "Wick", 40, 50000 };

oder

Employee e1{ { "John", "Wick", 40 }, 50000 };

oder wie @ Jarod42 in einem Kommentar zeigte, den Sie schreiben können

Employee e1{ { .name{"John"}, .surname{"Wick"}, .age{40} }, 50000 };

In diesem Fall wird die direkte Basisklasse durch eine festgelegte Initialisierungsliste initialisiert, während die Klasse Employe insgesamt durch eine nicht festgelegte Initialisierungsliste initialisiert wird.

Vlad aus Moskau
quelle
3
oder eine Mischung : Employee e1{ { .name{"John"}, .surname{"Wick"}, .age{40} }, 50000 };.
Jarod42
@ Jarod42 Ja, es wird kompiliert.
Vlad aus Moskau
5

Möglicherweise haben Sie mehrere Felder mit demselben Namen aus verschiedenen Basen.

Logischerweise sollten Sie den Namen der gewünschten Basis angeben, aber es scheint, dass es keine Möglichkeit gibt, dies zu tun.

// Invalid too:
Employee e1{.Person.name{"John"}, .Person.surname{"Wick"}, .Person.age{40}, .salary{50000}};
Employee e2{.Person{.name{"John"}, .surname{"Wick"}, .age{40}}, .salary{50000}};

Darüber hinaus ist die von C ++ festgelegte Initialisierung stärker eingeschränkt als C:

Hinweis: Festgelegte Initialisierung außerhalb der Reihenfolge, verschachtelte festgelegte Initialisierung, Mischen ausgewiesener Initialisierer und regulärer Initialisierer sowie festgelegte Initialisierung von Arrays werden in der Programmiersprache C unterstützt, sind jedoch in C ++ nicht zulässig.

Jarod42
quelle