C++:使用花括号初始化列表的函数调用表达式 - 标准是否规定在单元素列表的平凡情况下忽略花括号?

4

考虑下面的例子:

class X;

void f(const X &);

void g()
{
    X x;
    f({x});
}

标准是否要求在这种情况下忽略花括号?没有任何优化的情况下。如果是,那么是从哪个版本开始?

乍一看,按规则应该创建一个完全不必要的临时对象,但仍然会创建。查看列表初始化(list initialization)并未找到相关信息。这里的X不是聚合体。

使用-O0选项编译,无论是否存在可观测副作用的X复制构造函数或X具有X(std::initializer_list<X>)构造函数,GCC和Clang都会生成没有创建临时对象的代码。

1个回答

3
在调用函数 f({x}) 时,X const &x (即 f 的参数)的初始化属于复制初始化语境下的列表初始化,参考文献为 [dcl.init]/15。因此,我们可以省略这个函数,只问这意味着什么:
int main() {
    X x;
    X const &y = {x}; // still copy list initialization
}

现在,适用于这个问题的中的第一个条款是[dcl.init.list]/3.9,它规定你只需放弃括号。

... 如果初始化列表具有类型为E的单个元素,并且T [此处为X const&]不是引用类型,或其引用类型与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)。

请注意,上面的引用在您的cppreference页面上略有改动:

……(如果T不是类类型),如果大括号初始化列表仅有一个元素,并且T既不是引用类型,或者是引用类型,其引用类型与元素类型相同或是元素类型的基类,则T是直接初始化(在直接列表初始化中)或复制初始化(在复制列表初始化中)…..

也许是因为“T不是引用类型”或“T不是类类型”,这个问题没有引起注意,但这就是你正在寻找的条款,因为a)引用类型确实不是类类型,b)前提条件中的“或…”使其适用。鉴于缺少版本化框,这种行为应该和列表初始化本身一样古老:从C++11开始。


谢谢!的确,我忽略了那部分。 - Alexander Morozov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接