Ala*_*lan 10 c++ language-lawyer compiler-bug copy-initialization c++17
我最近了解到,一个类中可以有多个默认构造函数。然后我编写了以下程序,使用 msvc 进行编译,但是 clang 和 gcc 都无法编译它。
\nstruct A\n{\n\xc2\xa0 explicit A(int = 10);\n\xc2\xa0 A()= default;\n};\n\nA a = {}; //msvc ok but gcc and clang fails here\nRun Code Online (Sandbox Code Playgroud)\n\n我想知道根据 C++17 标准哪个编译器是正确的。
\n海湾合作委员会 说:
\n<source>:8:8: error: conversion from \'<brace-enclosed initializer list>\' to \'A\' is ambiguous\n 8 | A a = {}; //msvc ok but gcc and clang fails here\n | ^\n<source>:5:3: note: candidate: \'constexpr A::A()\'\n 5 | A()= default;\n | ^\n<source>:4:12: note: candidate: \'A::A(int)\'\n 4 | explicit A(int = 10);\nRun Code Online (Sandbox Code Playgroud)\n
太长了;
这是CWG 2856,该程序的格式符合标准中的当前措辞,如下所述。基本上,只有一个ctors(A::A()和explicit A::A(int)) 是转换构造函数。因此,只有前者才是A::A()可行的选择并且可以使用。
首先请注意,这A a = {};是复制初始化。
- 以大括号或等于初始化器或条件 ([stmt.select]) 的 = 形式发生的初始化,以及参数传递、函数返回、引发异常 ([ except.throw])、处理异常([except.handle])和聚合成员初始化([dcl.init.aggr])称为复制初始化。
接下来我们讨论初始化器的语义。
- 初始化器的语义如下。目标类型是正在初始化的对象或引用的类型,源类型是初始化表达式的类型。如果初始值设定项不是单个(可能带括号)表达式,则未定义源类型。
- 如果初始化器是(非括号)braced-init-list 或 is = braced-init-list,则对象或引用是列表初始化的 ([dcl.init.list])。
上面的意思是,该对象将被列表初始化。
从列表初始化:
列表初始化是对大括号初始化列表中的对象或引用进行初始化。这样的初始值设定项称为初始值设定项列表,初始值设定项列表的逗号分隔初始值设定项子句或指定初始值设定项列表的指定初始值设定项子句称为初始值设定项列表的元素。初始值设定项列表可能为空。列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,而复制初始化上下文中的列表初始化称为复制列表初始化。
上面的意思就是A a = {};复制列表初始化。接下来我们看看列表初始化的效果:
- T 类型的对象或引用的列表初始化定义如下:
- 否则,如果初始值设定项列表没有元素并且 T 是具有默认构造函数的类类型,则该对象将被值初始化。
上面的意思是该对象将被值初始化,所以我们继续进行值初始化:
- 对 T 类型的对象进行值初始化意味着:
- 如果 T 没有默认构造函数 ([class.default.ctor]) 或用户提供或删除的默认构造函数,则该对象是默认初始化的;
这意味着该对象将被默认初始化:
- 默认初始化 T 类型的对象意味着:
- 如果 T 是(可能是 cv 限定的)类类型 ([class]),则考虑构造函数。 枚举适用的构造函数 ([over.match.ctor]),并
initializer ()通过重载决策选择最佳的构造函数 ([over.match])。使用空参数列表调用如此选择的构造函数来初始化对象。
因此,我们继续使用over.match.ctor来获取候选 ctor 的列表。另请注意initializer()上面引用的参考文献中的部分,因为它将在最后用作参数。
- 当类类型的对象被直接初始化、从相同或派生类类型 ([dcl.init]) 的表达式复制初始化或默认初始化时,重载决策会选择构造函数。对于不在复制初始化上下文中的直接初始化或默认初始化,候选函数是正在初始化的对象的类的所有构造函数。对于复制初始化,候选函数是该类的所有转换构造函数。参数列表是初始化器的表达式列表或赋值表达式。
这意味着只有非显式 ctorA::A()是候选者,因为它是转换 ctor,而另一个则explicit A::A(int)不是。因此,候选者和可行选项集仅包含一个 ctor A::A()。
最后,由于我们只有一个可行的选项,因此它就是为初始化器 () 选择的选项。
| 归档时间: |
|
| 查看次数: |
477 次 |
| 最近记录: |