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



考虑以下代码:

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

这个程序是否符合c++ 11标准?在我的N3797副本上写着

8.5.4 List initialization [dcl.init.list]

3: T类型的对象或引用的list初始化定义如下:
—如果T是聚合,则按8.5.1进行聚合初始化。
—否则,如果Tstd::initializer_list<E>的专门化,则…
-否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并使用重载解析选择最佳构造函数。如果需要窄化转换来转换任何类型,则程序是病态的。
-否则,如果初始化列表中有一个类型为E的元素,并且T不是引用类型,或者它与E是引用相关的,则从该元素初始化对象或引用;如果需要窄化转换来将元素转换为T,则程序是病态的。
—否则,如果T是引用类型,则T引用的类型的pr值临时类型是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};
        ^

并且表示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, bar初始化,....,

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

这将使示例工作,因为当聚合初始化失败时,我们不匹配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.6, gcc-4.8, gcc-4.9中打印2,在clang-3.7, gcc-5.0中打印1

假设我错了,在c++ 11标准,聚合的列表初始化应该是聚合初始化,没有别的,直到引入缺陷报告中的新措辞,这是一个错误,即使我选择-std=c++11在较新的编译器上发生?

当标准写

"如果X,执行foo初始化。否则,如果执行Y, bar初始化,....否则,程序格式错误。",

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

不,它没有。就像实际的代码一样:

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

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

得到未定义行为

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

当标准写

"如果X,执行foo初始化。否则,如果Y,执行bar初始化,…

并不意味着如果X成立,但foo初始化不能成立执行,那么我们应该检查Y是否成立,然后尝试bar-initialization吗?

。如果X保持,则执行foo初始化。

最新更新