9

目标:能够对对象进行列表初始化,并使默认初始化将所有 POD 初始化为 0/ false,并尽可能少地使用样板代码 (C++11)。

假设我有几个带有几个 POD 的类(想想文件格式解析)。为了不处理未定义的值,我希望将默认构造对象的值初始化为 0。例如,提供我自己的默认 c'tormemset()this这种情况下可以正常工作,就像显式命名所有成员一样。

但是,这是样板文件。更重要的是,提供我自己的无参数默认构造函数会阻止我使用列表初始化语法进行成员初始化:

class Fails1 {
public:
  int a, b;
  Fails1() { memset(this, 0, sizeof(*this)); }
};

Fails1 this_works;
Fails1 this_fails{ 42, 54 }; // compiler error

我也可以为 the 添加构造函数initializer_list,但这更像是样板文件。我想避免所有这些样板。

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

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

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

// Example 1: default initialization
A a1;

第一个例子使用初始化;成员a并且b之后未定义(不是我想要的,我希望它们被值初始化)。

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

示例 2 使用值初始化,之后不复制。这就是我想要的,但是,我也认为空的大括号初始化列表会做同样的事情;请参见以下示例:

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

示例 3 是我认为可以解决问题的方法。8.5.4“列表初始化”中描述了使用空大括号初始化列表的列表初始化:;特别是 8.5.4.3 说:“如果初始化列表没有元素并且 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++ 中的一个错误。

所以回到我原来的问题。如何在不必提供我自己的样板默认构造函数的情况下对对象进行值初始化——同时保留对其成员使用列表初始化而不必提供我自己的initializer_list构造函数的能力?

4

2 回答 2

8

一个简单的结构,如

struct A { int a,b; };

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

GCC 警告非常具有误导性。

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

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

于 2012-12-25T14:37:05.630 回答
4

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 仍然错误地警告缺少初始化程序。

于 2012-12-25T14:49:35.133 回答