请考虑以下示例:
class X;
void f(const X &);
void g()
{
X x;
f({x});
}
在这种情况下,标准是否要求实现忽略大括号?不涉及任何优化。如果是,则从哪个版本开始?
乍一看,根据规则,似乎应该有一个临时创建的 - 当然完全没有必要,但仍然如此。查看列表初始化,我找不到任何相关内容。X
在这里不是聚合。
GCC 和 Clang,即使有-O0
,也无需临时创建即可生成代码 - 即使X
复制构造函数具有可观察到的副作用,即使X
具有X(std::initializer_list<X>)
构造函数。
f({x})
中X const &x
(f
的参数(的初始化是副本初始化上下文中的列表初始化,由[dcl.init]/15
。因此,我们可以删除该函数,只问这意味着什么:
int main() {
X x;
X const &y = {x}; // still copy list initialization
}
现在,[dcl.init.list]
中适用于此的第一个条款是[dcl.init.list]/3.9
,它指出您基本上只需删除大括号。
。如果初始值设定项列表具有
E
类型的单个元素,并且 [此处X const&
]T
不是引用类型,或者其引用类型与E
相关,则对象或引用从该元素初始化(通过复制初始化进行复制列表初始化,或通过直接初始化直接列表初始化(;....
X const&
实际上是一个引用类型,但其引用的类型X const
确实与初始值设定项的类型X
相关,因此该子句仍然适用。现在我们只有
int main() {
X x;
X const &y = x; // (non-list/"plain") copy-initialization
}
当然,这不会调用X
的构造函数(通过[dcl.init.ref]/5.1
(。
请注意,上面的引用在您的 cpp首选项页面上也略有改写:
。(如果
T
不是类类型(,如果大括号的初始化列表只有一个元素,并且T
不是引用类型,或者是引用类型与元素类型的基类相同或引用的类型,则T
直接初始化(在直接列表初始化中(或复制初始化(在复制列表初始化中(,....
也许"T
不是引用类型"或"T
不是类类型"使它飞到了雷达下,但这是您要查找的子句,因为 a( 引用类型确实不是类类型和 b( "或......"在前提条件使其适用。从缺少版本控制框来看,这种行为将与列表初始化本身一样古老:自 C++11 以来。