列出聚合的初始化:它什么时候可以调用复制构造函数?

list initialization of aggregates: when can it invoke copy constructor?

考虑以下代码:

struct A {
  int x;
};

int main() {
  A a;
  A b{a};
}

此程序是否符合 C++11 标准?在我的 N3797 副本中它说

8.5.4 List initialization [dcl.init.list]

3: List-initialization of an object or reference of type T is defined as follows:
- If T is an aggregate, aggregate initialization is performed (8.5.1).
- Otherwise, if T is a specialization of std::initializer_list<E>, ...
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen using overload resolution. If a narrowing conversion is required to convert any of the types, the program is ill-formed.
- Otherwise, if the initializer list has a single element of type E and either T is not a reference type or it is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion is required to convert the element to T, the program is ill-formed.
- Otherwise, if T is a reference type, a pr-value temporary of the type reference by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.
- Otherwise, if the initializer list has no elements, the object is value-initialized.
- Otherwise, the program is ill-formed.

示例的要点是,类型是聚合,但列表初始化应该调用复制构造函数。在 gcc 4.8gcc 4.9 上,在 C++11 标准下,它失败了:

main.cpp: In function ‘int main()’:
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization
   A b{a};
        ^

并说 A is not convertible to int 或类似的,因为聚合初始化失败。在 gcc 5.4 上,它在 C++11 标准下运行良好。

clang 上,您会遇到与 clang-3.53.6 类似的错误,并且它会在 clang-3.7 上开始工作。

我知道它在 C++14 标准中是良构的,并且在缺陷报告中提到了它 here

但是,我不明白为什么这被认为是标准中的缺陷。

标准写的时候,

"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, .... Otherwise, the program is ill-formed.",

这是否意味着如果X成立,但无法执行foo-initialization,那么我们应该检查Y是否成立,然后尝试bar-initialization?

这将使示例工作,因为当聚合初始化失败时,我们不匹配 std::initializer_list,我们匹配的下一个条件是“T 是 class 类型",然后我们考虑构造函数。

请注意,这 确实如此 似乎是它在这个修改后的示例中的工作方式

struct A {
  int x;
};

int main() {
  A a;
  const A & ref;
  A b{ref};
}

在 C++11 和 C++14 标准中,所有相同的编译器都以与前面示例相同的方式处理此问题。但似乎 CWG 缺陷记录中修改后的措辞不适用于此案例。上面写着:

If T is a class type and the initializer list has a single element of type cv T or a class type derived from T, the object is initialized from that element.

http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467

但在第二个代码示例中,初始化列表在技术上包含 const T &。所以我不知道它是如何工作的,除非在聚合初始化失败之后,我们应该尝试构造函数。

我错了吗?聚合初始化失败后是否不应该尝试构造函数?

这是一个相关的例子:

#include <iostream>

struct B {
  int x;

  operator int() const { return 2; }
};

int main() {
  B b{1};
  B c{b};
  std::cout << c.x << std::endl;
}

clang-3.6gcc-4.8gcc-4.9中打印2,在clang-3.7gcc-5.0中打印1.

假设我错了,并且在 C++11 标准中,聚合的列表初始化应该是聚合初始化而不是别的,直到引入缺陷报告中的新措辞,它是一个错误即使我 select -std=c++11 在较新的编译器上也会发生这种情况?

When the standard writes,

"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, ...

doesn't this mean that if X holds, but foo-initialization cannot be performed, then we should check if Y holds, and then attempt bar-initialization?

没有。如果 X 成立,我们将执行 foo 初始化。如果失败,则程序格式错误。

When the standard writes,

"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, .... Otherwise, the program is ill-formed.",

doesn't this mean that if X holds, but foo-initialization cannot be performed, then we should check if Y holds, and then attempt bar-initialization?

不,不是。把它想象成实际的代码:

T *p = ...;
if(p)
{
  p->Something();
}
else
{ ... }

p 不为 NULL。这也不意味着它是一个有效的指针。如果 p 指向一个被销毁的对象,p->Something() 失败不会导致您跳到 else。您有机会在这种情况下保护通话。

所以你得到了未定义的行为。

这里也是一样。如果 X,则执行 A。这并不意味着如果 A 失败会发生什么;它告诉你去做。如果做不到...你就完蛋了。