list聚合的初始化:什么时候可以调用拷贝构造函数?

Chr*_*eck 7 c++ aggregate language-lawyer c++11 list-initialization

请考虑以下代码:

struct A {
  int x;
};

int main() {
  A a;
  A b{a};
}
Run Code Online (Sandbox Code Playgroud)

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

8.5.4列表初始化 [dcl.init.list]

3:对象或类型引用的列表初始化T定义如下:
- 如果T是聚合,则执行聚合初始化(8.5.1).
- 否则,如果Tstd::initializer_list<E>,...的特化
- 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并使用重载决策选择最佳构造函数.如果转换任何类型需要缩小转换,则程序格式错误.
- 否则,如果初始化列表具有单个元素类型E且要么T不是引用类型,要么与引用相关E,则从该元素初始化对象或引用; 如果将元素转换为需要缩小转换T,则程序格式错误.
- 否则,如果T是引用类型,则类型引用的临时值pr T是copy-list-initialized或direct-list-initialized,具体取决于引用的初始化类型,引用绑定到该临时.
- 否则,如果初始化列表没有元素,则对象进行值初始化.
- 否则,该程序格式不正确.

示例的要点是,类型是聚合,但列表初始化应该调用复制构造函数.在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};
        ^
Run Code Online (Sandbox Code Playgroud)

并说A is not convertible to int或类似,因为聚合初始化失败.在gcc 5.4,它在C++ 11标准下工作正常.

clang你得到类似的错误有clang-3.5,3.6,并开始在工作clang-3.7.

我知道它在C++ 14标准中已经很好地形成了,并且在这里的缺陷报告中提到了它.

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

当标准写入时,

"如果X,执行foo初始化.否则,如果执行Y条形初始化,......否则,程序格式不正确.",

这是不是意味着如果X保持,但foo初始化不能执行,那么我们应该检查是否Y保持,然后尝试条形初始化?

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

请注意,这似乎是它是如何工作在这个变型实例

struct A {
  int x;
};

int main() {
  A a;
  const A & ref;
  A b{ref};
}
Run Code Online (Sandbox Code Playgroud)

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

如果T是类类型且初始化列表具有单个元素类型cv T或派生自的类类型T,则从该元素初始化该对象.

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;
}
Run Code Online (Sandbox Code Playgroud)

clang-3.6,gcc-4.8,gcc-4.9,它打印2,并且clang-3.7,gcc-5.0它打印1.

假设我错了,并且在C++ 11标准中,聚合的列表初始化应该是聚合初始化而不是其他任何东西,直到引入缺陷报告中的新措辞,即使我发生这种情况也是如此选择-std=c++11较新的编译器?

Mar*_*ica 3

标准写的时候,

“如果X,则执行 foo 初始化。否则,如果Y,则执行 bar 初始化,...

这是否意味着 ifX成立,但无法执行 foo 初始化,那么我们应该检查是否Y成立,然后尝试 bar 初始化?

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