【问题标题】:List initialization and copy elision列表初始化和复制省略
【发布时间】:2014-04-22 21:30:12
【问题描述】:

考虑以下示例:

#include <cstdlib>
struct A
{
    A(int, char*){};
    A(const A&){ printf("copy-ctor\n"); }
};
int main()
{
    A x = A(5, nullptr);
}

根据 8.5.16(C++11 标准)行

    A x = A(5, nullptr);

被视为

    A x(A(5, nullptr));

(即创建一个 A 类型的临时对象并将其传递给 A 类型的 copy-ctor 以初始化 x)。然后根据 12.8.31,允许编译器(但不强制)执行称为“复制省略”的优化,以消除创建 A 类型的临时代码,从而有效地使该行代码变为

    A x(5, nullptr);

(即没有创建临时对象,也没有调用 copy-ctors)。

现在,假设我在上面的示例中使用了列表初始化,如下所示:

    A x = {5, nullptr}; // (1)

    A x = A{5, nullptr}; // (2)

有人可以引用 C++11 标准中的适当段落,确认或否认 (1) 和/或 (2) 将总是(即不仅当编译器可以进行“复制省略”优化时)被视为

    A x(5, nullptr);

(即直接调用 A 的第一个构造函数,不创建临时对象,不复制 A 类型的对象)。

【问题讨论】:

    标签: c++ c++11 language-lawyer copy-elision list-initialization


    【解决方案1】:

    这个答案显然是错误的,这让我很惊讶。见 cmets。我认为 [dcl.init.list]/3 的第一个和第四个要点是什么意思(1)直接调用构造函数(或执行聚合初始化),而不是临时的。

    标准中没有任何内容可以保证 (1) 和 (2) 避免临时性。它们都是复制初始化,(1)是复制列表初始化,由[dcl.init.list] p1定义:

    列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为direct-list-initialization,复制初始化上下文中的列表初始化称为copy-list-initialization。 p>

    在这两种情况下都是复制初始化,[dcl.init] 说这可能涉及移动(可以省略)。

    8.5/14,15:

    表单中发生的初始化

    T x = a;

    [...] 称为复制初始化。

    表单中发生的初始化

    T x(a);

    T x{a};

    [...] 称为直接初始化。

    如果您的编译器不够聪明,无法始终忽略临时,那么为了确保没有临时列表初始化,您可以使用直接列表初始化,即

    A x{5, nullptr};
    

    【讨论】:

    • @Jonathan 不知何故,我的印象是“统一初始化”(除其他外)旨在消除直接初始化和复制初始化之间的差异(并且拼命地试图在标准中找到确认)。既然你确认不是这样,我就会问心无愧地睡觉。 :) 谢谢!
    • 乔纳森,这是不正确的。 A x = {5, nullptr}; 不会涉及移动。当然,只有A x = A{5, nullptr}; 会涉及移动(可以省略),因为他明确地创建了临时对象。
    • @litb,谢谢,我没有意识到,答案已更新
    猜你喜欢
    • 2022-11-09
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    • 2014-03-16
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多