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

时间:2016-08-17 20:27:16

标签: c++ c++11 aggregate language-lawyer list-initialization

请考虑以下代码:

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>的专业化,...       - 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并使用重载决策选择最佳构造函数。如果转换任何类型需要缩小转换,则程序格式不正确       - 否则,如果初始化列表包含E类型的单个元素且T不是引用类型或与E引用相关,则对象或引用初始化自那个元素;如果将元素转换为T需要缩小转换,则程序格式不正确       - 否则,如果T是引用类型,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};
        ^

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

clang上,clang-3.53.6会出现类似错误,并且会在clang-3.7开始工作。

据我所知,它在C ++ 14标准中已经很好地形成了,并且在缺陷报告here中提到了它。

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

标准写入时,

“如果X,则执行foo初始化。否则,如果Y,执行条形初始化,......否则,程序格式错误。”,

这不意味着如果X成立,但无法执行foo初始化,那么我们应该检查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

2 个答案:

答案 0 :(得分:4)

  

标准写入时,

     

&#34;如果执行X,foo初始化。否则,如果执行Y,条形初始化,......否则,程序格式不正确。&#34;,

     

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

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

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

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

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

同样如此。如果是X,那么做A.这并不意味着如果A失败会发生什么;它告诉你这样做。如果它无法完成......你就搞砸了。

答案 1 :(得分:3)

  

标准写入时,

     

&#34;如果X,则执行foo初始化。否则,如果Y,   执行条形初始化,...

     

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

没有。如果X成立,我们执行foo初始化。如果失败,程序就会形成错误。