web-dev-qa-db-fra.com

Initialiseurs désignés en C ++ 20

J'ai une question sur l'une des fonctionnalités c ++ 20, les initialiseurs désignés (plus d'informations sur cette fonctionnalité ici )

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

Ce code a été compilé avec gcc 9.2.0 et -Wall -Wextra -std=gnu++2a drapeaux.

Comme vous pouvez le voir ci-dessus, les deux structures, Person et Employee sont des agrégats mais l'initialisation de Employee agrégat n'est pas possible à l'aide des initialiseurs désignés.

Quelqu'un pourrait-il m'expliquer pourquoi?

23
MateuszGierczak

Selon la norme C++ 20 (9.3.1 Agrégats. P. # 3)

(3.1) - Si la liste d'initialiseurs est une liste d'initialiseurs désignés, l'agrégat doit être de type classe, l'identifiant dans chaque désignateur doit nommer un membre de données direct non statique de la classe, et les éléments explicitement initialisés de l'agrégat sont les éléments qui sont ou contiennent ces membres.

Vous ne pouvez donc pas utiliser la liste d'initialisation désignée pour initialiser les membres de données des classes de base.

Utilisez plutôt l'initialisation de liste habituelle comme

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

ou

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

ou comme @ Jarod42 indiqué dans un commentaire que vous pouvez écrire

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

Dans ce cas, la classe de base directe est initialisée par une liste d'initialisation désignée tandis que la classe Employe dans son ensemble est initialisée par une liste d'initialisation non désignée.

14
Vlad from Moscow

Vous pouvez avoir plusieurs champs avec le même nom de différentes bases,

donc logiquement, vous devez fournir le nom de la base recherchée, mais il semble qu'il n'y ait aucun moyen de le faire.

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

De plus, l'initialisation désignée par C++ est plus contrainte que C:

Remarque: l'initialisation désignée dans le désordre, l'initialisation désignée imbriquée, le mélange des initialiseurs désignés et des initialiseurs réguliers et l'initialisation désignée des tableaux sont tous pris en charge dans le langage de programmation C, mais ne sont pas autorisés en C++.

5
Jarod42