空的大括号init列表会发出关于缺少字段初始值设定项的警告

empty brace-init-lists emit warnings about missing field initializers

本文关键字:字段 警告 于缺少 出关 init 列表      更新时间:2023-10-16

目标:能够列出初始化对象并具有默认值初始化初始化将所有POD初始化为0/false,只需少量样板尽可能(C++11(。

假设我有几个类和几个POD(想想文件格式解析(。为了不处理未定义的值,我希望要将值初始化为0的默认构造对象。例如提供我自己的默认c'或memset()this可以正常工作在这种情况下,明确命名所有成员也是如此。

然而,这只是样板。更重要的是提供我自己的无参数默认构造函数阻止我使用列表成员初始化的初始值设定项语法:

class Fails1 {
public:
  int a, b;
  Fails1() { memset(this, 0, sizeof(*this)); }
};
Fails1 this_works;
Fails1 this_fails{ 42, 54 }; // compiler error

我也可以添加initializer_list的构造函数,但是这更像是样板。我想避开那些样板房。

因此,我查看了编译器在没有用户提供的默认构造函数与各种方法如何初始化它们。这是我得到的完全困惑:

class A {
public:
  int a, b;
};

有了这个类,我可以使用空的初始化列表和具有值,而不必自己提供两个构造函数;那个是我想要的一部分。

// Example 1: default initialization
A a1;

第一个示例使用初始化;成员CCD_ 5和CCD_之后未定义(不是我想要的,我希望它们是值已初始化(。

// Example 2: Value-initialization, so this works, I guess:
A a2 = A();

示例2使用值初始化,之后不进行复制。所以这就是我想要的,然而,我也认为空的大括号init列表也可以这样做;请参阅以下示例:

// Example 3: list-initialization with empty brace-init-list
A a3{};

示例3是我认为会起作用的。列表初始化8.5.4中描述了带有空大括号的init列表"列表初始化":;特别是8.5.4.3说:"如果初始化器list没有元素,并且T是具有默认构造函数的类类型,objectvalue initialized"。但是,这会在g++4.7.2中发出警告带有-Wextra:

missing initializer for member ‘A::a’ [-Wmissing-field-initializers]
missing initializer for member ‘A::b’ [-Wmissing-field-initializers]

clang++3.1没有警告,所以它也可能是g++中的一个bug。

回到我最初的问题。如何初始化对象而不必提供我自己的样板文件默认构造函数--同时保留对其成员使用列表初始化的能力而不必提供我自己的initializer_list构造函数?

类似的普通结构

struct A { int a,b; };

显然是一个聚合,A a {};显然是聚合初始化,并暗示值初始化(即零初始化(,如您已经引用的§8.5.4/3所述。(请特别注意下一段中给出的例子,它实际上与您的情况完全相同。(

GCC的警告非常具有误导性。

在搜索现有的bug报告时,我发现了这个,它是您自己提交的。我认为这是正确的做法。

既然您已经制定了相关的标准部分,并且其他人也在评论中做出了贡献,我将把它作为社区Wiki的答案。

gcc关于缺少初始值设定项的警告比其他任何事情都更有害:如果有一个初始值设定值列表,则从列表中填充值,其余字段初始化为零。因此,对于空的初始化器列表,所有值都是零初始化的。警告一个没有提到所有成员的非空初始值设定项列表可能是合理的:我看到过一些错误,程序员为数组的第一个元素提供了一个非零初始值设定值,并假设对所有剩余值都会重复该初始值设定器。

关于在默认情况下创建初始化,同时也允许初始值设定项列表,我会为基本类型使用一个初始化模板:

template <typename T>
struct init
{
    init(): value_() {}
    template <typename S> init(S&& value): value_(std::forward<S>(value)) {}
    T value_;
};
struct foo
{
    init<int>    i_;
    init<double> d_;
    std::string  s_;
};
int main()
{
    foo f0;
    foo f1 = { 1, 3.14, "foo" };
    foo f2 = { };
}

这种方法保证了成员的初始化独立于类的对象的使用方式,同时还支持使用初始化器列表。不过,gcc仍然错误地警告缺少初始化程序。