使用统一初始化来构造对象的简单程序无法编译

And*_*owl 6 c++ constructor initialization uniform-initialization c++11

考虑以下程序:

struct X
{
    X(int, int) { }
    X(X&&) { }
};

int main()
{
    X x( {0, 1} ); // Doesn't compile on ICC 13.0.1, compiles on
                   // Clang 3.2, GCC 4.7.2, and GCC 4.8.0 beta.
}
Run Code Online (Sandbox Code Playgroud)

使用GCC 4.7.2,GCC 4.8.0和Clang 3.2编译时,该程序执行以下操作(*):

  1. 构造一个临时类型X传递值01构造函数,然后;
  2. X从那个临时移动构造.

相反,使用ICC 13.0.1,它不会编译.

问题#1:谁是对的?

(*)实际上,临时的创建和对移动构造函数的调用都被省略了,但是使用该-fno-elide-constructors选项进行编译并向构造函数添加一些打印输出可以发现这是正在发生的事情.


现在考虑以下,上述程序的轻微变化,其中统一初始化用于直接初始化x:

int main()
{
    X x{ {0, 1} }; // ERROR! Doesn't compile.
//     ^........^
}
Run Code Online (Sandbox Code Playgroud)

我不希望在这里使用大括号而不是括号来改变任何东西,但不知何故:这个程序不能在我测试过的任何编译器上编译(Clang 3.2,GCC 4.7.2,GCC 4.8. 0 beta,和ICC 13.0.1).

问题#2:为什么?

Pot*_*ter 2

这只是所有编译器中的一个错误。\xc2\xa78.5.4/3 说,

\n\n
\n

T 类型的对象或引用的列表初始化定义如下:

\n\n

\xe2\x80\x94 如果初始值设定项列表没有元素并且 T 是具有默认构造函数的类类型,则该对象将被值初始化。

\n\n

\xe2\x80\x94 否则,如果 T 是聚合,则执行聚合初始化 (8.5.1)。

\n\n

\xe2\x80\x94 否则,如果 T 是 std::initializer_list 的特化,则按如下所述构造一个initializer_list 对象并用于初始化对象 \xe2\x80\xa6

\n\n

\xe2\x80\x94 否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策选择最好的构造函数(13.3、13.3.1.7)。如果需要缩小转换(见下文)来转换任何参数,则该程序格式错误。

\n\n

\xe2\x80\x94 否则,如果 T 是引用类型,则 T 引用的类型的纯右值临时值将被列表初始化,并且引用将绑定到该临时值。

\n\n

\xe2\x80\x94 否则,如果初始化列表只有一个元素,则从该元素初始化对象或引用;如果需要缩小转换(见下文)将元素转换为 T,则该程序格式错误。

\n\n

\xe2\x80\xa6

\n
\n\n

不少案例。请注意,您实际上是在列表初始化绑定到右值引用的纯右值临时对象。

\n\n

至于 GCC,我认为它正在尝试将上面提到的最后一项应用于单元素初始值设定项列表。如果我将构造函数签名更改为X(X&&, int = 3),则初始化程序{ {0, 1} }将失败,但是{ {0, 1}, 3 }会成功。单个项目应该成功,因为该元素是一个花括号初始化列表,并且我相信这种情况应该允许额外的封闭大括号,类似于括号。但这个失败与 GCC 的大括号省略的其他缺点类似。

\n\n

我的总体印象是,当编译器尝试将列表视为具有类型的对象时,就会出现问题,但事实并非如此。很难将其转换回类似带括号的参数列表之类的东西。

\n\n

更具体地查看错误消息(感谢 LWS 链接),

\n\n
    \n
  • 国际商会坚称需要表达意见。这是错误的,因为根据基本语法,花括号初始化列表可以包含其他花括号初始化列表,而不仅仅是表达式。

  • \n
  • Clang 说“候选构造函数不可行:无法将初始值设定项列表参数转换为 \'X\'”,但如果使用显式转换,则它可以工作X x{ X{0, 0 } };。这没有道理。这不是转换,因为列表没有可供转换的类型。这是列表初始化。

  • \n
  • GCC 表示“没有已知的参数 1 从 \'\' 到 \'X&&\' 的转换”,表明它甚至没有达到定义一个临时对象来绑定到引用的程度。与 Clang 一样,它似乎正在尝试虚假转换,并指定X{0,0}修复它。

  • \n
\n