构造函数会干扰指定为初始值设定项的成员变量吗?

时间:2018-09-11 18:39:30

标签: c++ g++ language-lawyer c++20 designated-initializer

一段时间以来,人们已经能够在GCC中使用“指定的初始化程序”:

struct CC{
    double a_;
    double b_;
};

CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.bla = 0., .bli = 0.}; // compile error

但是,当我添加构造函数时,标签将被忽略。

struct CC{
    double a_;
    double b_;
    CC(double a, double b) : a_{a}, b_{b}{}
};

CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.b_ = 2., .a_ = 1.}; // compiles but labels don't matter only the order, confusing
CC cc{.bla = 2., .bli = 1.}; // compiles but labels don't matter, confusing

换句话说,带有构造函数的初始化程序语法使标签的行为就像注释!,这可能非常令人困惑,但最重要的是,这很奇怪。

我偶然发现了gcc 8.1 -std=c++2a

这是预期的行为吗?

参考:https://en.cppreference.com/w/cpp/language/aggregate_initialization

2 个答案:

答案 0 :(得分:7)

指定的初始值设定项的标签不应被编译器忽略。带有构造函数的所有这三个示例都应格式错误。

由于C ++ 20指定的初始值设定项功能与GCC的C样式的指定初始值设定项之间的冲突,您可能会收到此行为,由于GCC只是将它们提供给您,您将隐式访问它们。如果GCC是正确的C ++ 20编译器,则一旦为该类型提供构造函数,它将不再是聚合,因此指定的初始化方法用法将格式错误。

基本上,这是由编译器默认提供的非C ++标准行为引起的驱动程序错误。很有可能,如果关闭此功能,则在这种情况下会得到正确的编译器错误。

答案 1 :(得分:5)

这是一个gcc错误,仍然会生成even with -pedantic,其中we should receive warnings for any extensions

  

...要获取该标准所需的所有诊断,还应指定-pedantic ...

并且gcc声称支持P0329R4: Designated initializers模式according to the C++ Standards Support in GCC pageC++2a建议:

  

语言功能|提案|在GCC中可用吗?
  ...
  指定的初始化程序| P0329R4 | 8

要使用Designated initializers,该类型应汇总为[dcl.init.list]p3.1

  

如果括号初始列表包含指定的初始化列表,则 T应为聚合类。有序   指定的初始化程序列表的指示符中的标识符应构成命令的子序列   T的直接非静态数据成员中的标识符。执行聚合初始化(11.6.1)。   [示例:

struct A { int x; int y; int z; };
A a{.y = 2, .x = 1}; // error: designator order does not match declaration order
A b{.x = 1, .z = 2}; // OK, b.y initialized to 0
     

-结束示例]

CC不是[dcl.init.aggr]的汇总:

  

聚集是具有
的数组或类(第12条)   -(1.1)— 没有用户提供的,显式或继承的构造函数(15.1),
  ....

gcc错误报告

如果我们查看gcc bug report: Incorrect overload resolution when using designated initializers,则会在此示例中看到:

  

另一个测试用例,它从Chromium 70.0.3538.9减少到并被   叮当声:

  struct S { void *a; int b; };
  void f(S);
  void g() { f({.b = 1}); }
     

此操作失败

  bug.cc: In function ‘void g()’:
  bug.cc:3:24: error: could not convert ‘{1}’ from ‘<brace-enclosed initializer list>’ to ‘S’
   void g() { f({.b = 1}); }
                        ^
     

该错误表明字段名称在此期间被完全忽略   重载解析,这也解释了   最初报告的代码。

似乎gcc在重载解析期间会忽略字段名称。这可以解释您所看到的奇怪行为,并且在删除构造函数时我们将获得正确的诊断。