Wyznaczone inicjalizatory w C ++ 20

25

Mam pytanie dotyczące jednej z funkcji c ++ 20, wyznaczonych inicjatorów (więcej informacji o tej funkcji tutaj )

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

Ten kod został skompilowany z gcc 9.2.0 i -Wall -Wextra -std=gnu++2aflagami.

Jak widać powyżej, obie struktury Personi Employeesą agregacjami, ale inicjowanie Employeeagregacji nie jest możliwe przy użyciu wyznaczonych inicjatorów.

Czy ktoś mógłby mi wyjaśnić, dlaczego?

MateuszGierczak
źródło
Nie wiem, czy to rozwiąże twój problem, ale nie możesz odziedziczyć tutaj publicznego ...struct Employee : public Person
skratchi.at
@ skratchi.at stackoverflow.com/a/3965003/11683
GSerg
@GSerg Ok, cóż ... Nigdy nie pomyślałem o tym, ponieważ używam publiclub za privatekażdym razem ... dzięki w każdym razie
skratchi.at
jaki masz dokładny błąd?
skratchi.at
2
@ skratchi.at structs domyślnie dziedziczy publicznie
idclev 463035818

Odpowiedzi:

15

Zgodnie ze standardem C ++ 20 (9.3.1 Aggregates. S. 3)

(3.1) - Jeżeli lista inicjalizacyjna jest listą wyznaczoną do inicjalizacji, agregat musi być typu klasy, identyfikator w każdym oznaczeniu powinien określać bezpośredni niestatyczny element danych klasy oraz jawnie zainicjowane elementy agregatu są elementami, które są lub zawierają te elementy.

Dlatego nie można używać wyznaczonej listy inicjalizacyjnej do inicjowania elementów danych klas podstawowych.

Zamiast tego użyj zwykłej inicjalizacji listy, takiej jak

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

lub

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

lub jak wskazał @ Jarod42 w komentarzu, który możesz napisać

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

W tym przypadku bezpośrednia klasa podstawowa jest inicjowana przez wyznaczoną listę inicjalizującą, podczas gdy klasa Employe w całości jest inicjowana przez niewyznaczoną listę inicjalizującą.

Vlad z Moskwy
źródło
3
lub mix: Employee e1{ { .name{"John"}, .surname{"Wick"}, .age{40} }, 50000 };.
Jarod42
@ Jarod42 Tak, kompiluje się.
Vlad z Moskwy
5

Możesz mieć kilka pól o tej samej nazwie z różnych baz,

więc logicznie powinieneś podać nazwę poszukiwanej bazy, ale wygląda na to, że nie ma na to sposobu.

// 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}};

Ponadto inicjalizacja wyznaczona w C ++ jest bardziej ograniczona niż C:

Uwaga: wyznaczona inicjalizacja poza kolejnością, wyznaczona inicjalizacja zagnieżdżona, mieszanie wyznaczonych inicjatorów i inicjatorów regularnych oraz wyznaczona inicjalizacja tablic są obsługiwane w języku programowania C, ale nie są dozwolone w C ++.

Jarod42
źródło