【问题标题】:empty brace-init-lists emit warnings about missing field initializers空的大括号初始化列表会发出有关缺少字段初始值设定项的警告
【发布时间】:2012-12-25 12:00:40
【问题描述】:

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

假设我有几个类和几个 POD(想想文件格式 解析)。为了不处理未定义的值,我想要 默认构造对象的值初始化为 0。例如, 提供我自己的默认 c'tor memset()s 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;

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

// 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 说:“如果初始化 list 没有元素并且 T 是具有默认构造函数的类类型, 对象值初始化”。但是,这会在 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 构造函数?

【问题讨论】:

  • 我不觉得A() : a{}, b{} {} 太样板了,但如果这些警告不存在并且A a{} 编译时没有警告会更好。
  • 提供默认ctor还需要我提供initializer_list默认ctor。现在将其乘以每个班级 > 20 个标题字段乘以 > 10 个班级,我肯定会称其为样板。当然这是旁观者的看法,所以我的样板文件很可能是您的正常编码风格,这完全可以:)
  • 在 C++03 中根本不允许在构造函数中使用 memset,因为用户定义的构造函数的存在使类成为非 POD。在 C++11 中,这是有点放松的,因此在某些情况下是允许的。但是,我看到一把大枪正对准你的脚。
  • @MoritzBunkus,说实话,到那时,我可能会禁用警告。这与你在做什么无关。
  • @BoPersson:好的,不知道这一点,但这让我的问题对我来说更加重要,以免产生太多样板。

标签: c++ c++11


【解决方案1】:

这样的普通结构
struct A { int a,b; };

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

GCC 警告非常具有误导性。

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 感谢您提供信息并提供故障安全替代方案。
    猜你喜欢
    • 1970-01-01
    • 2020-12-10
    • 1970-01-01
    • 2011-05-31
    • 2012-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多