#include <iostream>
#include <initializer_list>
using namespace std;
struct CL
{
CL(){}
CL (std::initializer_list<CL>){cout<<1;}
CL (const CL&){cout<<2;}
};
int main()
{
CL cl1;
CL cl2 {cl1}; //prints 21
}
这是带有复制构造函数和初始化列表构造函数的 CL 结构。我认为这里只能调用复制构造函数,因为根据C ++ 14 Standard,8.5.4 / 3
对象或类型T的引用的列表初始化定义为 如下:
- 如果T是类类型且初始化列表具有 cv U类型的单个元素,其中U是T或从T派生的类, 该对象从该元素初始化(通过复制初始化 用于复制列表初始化,或通过直接初始化 直接列表初始化)。
- 否则,......
换句话说, cl2 的初始化必须从 cl1 元素执行,而不是从初始化列表 {cl1} 执行。 Clang和gcc都打印&#34; 21&#34;只有Visual Studio打印&#34; 2&#34;我认为这是对的。
有两个候选构造函数用于获取CL类型的参数 cl1 :
1)具有std::initializer_list<CL>
的构造函数(因为没有从CL到std::initializer_list<CL>
的转换而通过)
2)使用const CL&amp ;;复制构造函数。 (完全匹配只有资格转换非const-&gt; const)
谁是对的?谁的行为是正确的?
由于
答案 0 :(得分:2)
tl; dr:已发布的C ++ 14文本指定了输出21
。但是,CWG Issue 1467更改了此代码的行为,并于2014年11月获得了“缺陷”状态。
缺陷报告被视为追溯适用。 clang 3.7和VS2015已经应用了此缺陷报告建议的分辨率,该报告出现在N4296的C ++ 17草案中。
在此缺陷报告之前,N4140 [over.match.list]中的该文字涵盖了该行为:
当非聚合类类型
T
的对象进行列表初始化(8.5.4)时,重载决策会分两个阶段选择构造函数:
- 最初,候选函数是类T的初始化列表构造函数(8.5.4),参数列表由初始化列表作为单个参数组成。
- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成。
如果初始化列表没有元素且T具有默认构造函数,则省略第一阶段。在copy-list-initialization中,如果选择了显式构造函数,则初始化是错误的。 [注意:这与其他情况(13.3.1.3,13.3.1.4)不同,其中只考虑转换构造函数进行复制初始化。此限制仅适用于此初始化是重载解析的最终结果的一部分。 - 后注]
您的类不是聚合,因为它具有用户提供的构造函数。
以上文字由[dcl.init.list] / 3中的以下要点指示:
- 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。
因此,在已发布的C ++ 14中,初始化列表构造函数实际上应该优先于复制构造函数(如果匹配)。 C ++ 11有相同的文字。
在你的问题中,你说C ++ 14包含:
如果
T
是类类型且初始化列表具有[...]类型的单个元素
此文本不是在C ++ 14中,而是在以后由缺陷报告应用。在应用了缺陷报告的更新标准中(N4296),这在[dcl.init.list] / 3中的项目符号列表中显示为更高的项目符号点;所以现在复制构造函数在过程中选择得更早,我们没有达到上述[over.match.list]步骤。
请注意,虽然缺陷的标题是从同类型对象中进行聚合的列表初始化,但该分辨率实际上会影响聚合和非聚合的初始化。