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

考虑下面的代码:

struct A {
  int x;
};

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

这个程序在C ++ 11标准中是否格式良好? 它说,在我的N3797副本

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

3:类型T的对象或引用的列表初始化定义如下:
- 如果T是聚合,则执行聚合初始化(8.5.1)。
- 否则,如果Tstd::initializer_list<E>的专门std::initializer_list<E> ,...
- 否则,如果T是类类型,则考虑构造函数。 枚举适用的构造函数并使用重载解析来选择最好的构造函数。 如果需要缩小转换来转换任何类型,则该程序不合格。
- 否则,如果初始化器列表具有E类型的单个元素,并且T不是引用类型,或者它与E有关的引用相关,则该对象或引用将从该元素初始化; 如果需要缩小转换将元素转换为T ,则该程序不合格。
- 否则,如果T是引用类型,则由T引用的类型引用的pr-value临时值将根据引用的初始化类型进行复制列表初始化或直接列表初始化,并且引用绑定到那是暂时的。
- 否则,如果初始化程序列表中没有元素,则对该对象进行值初始化。
- 否则,该计划不合格。

该示例的要点是,该类型是一个聚合,但列表初始化应该调用复制构造函数。 在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标准中是正确的,并且在这里的缺陷报告中提到了它。

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

当标准写道时,

“如果执行X ,foo初始化,否则,如果执行Y ,则执行条形初始化,否则程序不合格。”,

这不是说如果X持有,但是foo-initialization不能执行,那么我们应该检查Y成立,然后尝试条形初始化?

这会让这个例子工作,因为当聚集初始化失败时,我们不匹配std::initializer_list ,并且匹配的下一个条件是“ T是类类型”,然后我们考虑构造函数。

请注意,这似乎是它在这个修改后的示例中的工作原理

struct A {
  int x;
};

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

在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;
}

clang-3.6gcc-4.8gcc-4.9 ,它打印2 ,而在clang-3.7gcc-5.0打印1

假设我错了,并且在C ++ 11标准中,聚合的列表初始化应该是聚合初始化,而不是别的,直到引入缺陷报告中的新语句,是否会发生这种错误,即使当我在较新的编译器上选择-std=c++11


当标准写道时,

“如果执行X ,foo初始化,否则,如果执行Y ,则执行条形初始化...

这不是说如果X持有,但是foo-initialization不能执行,那么我们应该检查Y成立,然后尝试条形初始化?

不。如果X成立,我们执行foo初始化。 如果这个失败了,那么这个计划是不合格的。


当标准写道时,

“如果执行X,foo初始化,否则,如果执行Y,则执行条形初始化,否则程序不合格。”,

这不是说如果X持有,但是foo-initialization不能执行,那么我们应该检查Y是否成立,然后尝试条形初始化?

不,不是的。 想想它像实际的代码:

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

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

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

这里也是一样。 如果X,做A.这并不意味着如果A失败会发生什么; 它告诉你这样做。 如果不能完成......你被搞砸了。

链接地址: http://www.djcxy.com/p/67793.html

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

下一篇: Value initialization of nested structs does not work properly