3

考虑以下示例:

#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 类型的对象)。

4

1 回答 1

3

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

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

列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化

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

8.5/14,15

表单中发生的初始化

T x = a;

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

表单中发生的初始化

T x(a);

T x{a};

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

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

A x{5, nullptr};
于 2014-03-17T15:56:23.170 回答