C++20中的指定初始化器

40

我有一个关于C++20特性的问题,即指定初始化器(更多关于这个特性的信息请参见此处)。

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

这段代码是使用gcc 9.2.0编译的,并使用了-Wall -Wextra -std=gnu++2a标志。

正如您在上面看到的那样,PersonEmployee两个结构体都是聚合体,但是无法使用指定初始化程序来初始化Employee聚合体。

有人能解释一下为什么吗?


1
我不知道它是否解决了你的问题,但是在这里你可能不需要继承公共部分... struct Employee : public Person - skratchi.at
@skratchi.at https://dev59.com/bVHTa4cB1Zd3GeqPPzwE#3965003 - GSerg
@GSerg 好的,嗯...我从来没有考虑过这个问题,因为我每次都使用publicprivate ... 不管怎样,还是谢谢。 - skratchi.at
你得到的确切错误是什么? - skratchi.at
1
在SO上有一个类似的问题。但它似乎回答了为什么它不起作用。https://dev59.com/ZWAg5IYBdhLWcg3wQpBX - skratchi.at
如果您想查看我遇到的错误,请使用在线编译器等工具。您需要做的只是复制并粘贴此代码(并使用适当的gcc版本和编译器标志)。不幸的是,在c ++17/20中,“继承pod的大括号初始化”中描述的问题无效。 - MateuszGierczak
2个回答

33
根据C++ 20标准(9.3.1聚合体。第3页),如果初始化列表是指定的初始化列表,则聚合体必须是类类型,每个指示符中的标识符都必须命名类的直接非静态数据成员,并且聚合体的显式初始化元素是那些是这些成员或包含这些成员的元素。 因此,您不能使用指定的初始化程序列表来初始化基类的数据成员。请改用通常的列表初始化方式。
Employee e1{ "John", "Wick", 40, 50000 };


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

或者像@Jarod42在评论中指出的那样,您可以编写:

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

在这种情况下,直接基类通过指定的初始化器列表进行初始化,而整个Employe类则通过非指定的初始化器列表进行初始化。


9
或者混合方式:员工 e1{ { .姓名{"约翰"}, .姓氏{"威克"}, .年龄{40} }, 50000 }; - Jarod42
1
@Jarod42 是的,它可以编译。 - Vlad from Moscow
不,它无法编译。 - Carlo Wood
@CarloWood 是的 它编译通过 - Ted Lyngmo

13

你可能有多个来自不同数据源的同名字段,

因此逻辑上,你应该提供所需的数据源名称,但似乎没有办法这样做。

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

此外,C ++ 指定初始化比 C 的更受限制:

注意:C 编程语言支持无序指定初始化、嵌套指定初始化、混合指定初始化器和常规初始化器以及数组的指定初始化,但这些在 C++ 中不被允许。


2
.age{40}会产生braces around scalar initializer警告,对吗?你知道为什么会发生这种情况以及如何避免吗? - NutCracker
@NutCracker 因为-Wbraced-scalar-init(但它并没有说明为什么会有这个警告)。要么改成.age = 40,要么使用-Wno-braced-scalar-init关闭警告。 - Ted Lyngmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接