为什么带大括号的复制初始化会省略复制/移动构造?

Mag*_*ero 4 c++ initialization language-lawyer copy-elision c++14

这是一个 C++ 14 程序,用于比较直接初始化(no =)和复制初始化=):*

#include <iostream>


struct A {
    A(int) { std::cout << "A(int)" << std::endl; }
    A(A&) { std::cout << "A(A&)" << std::endl; }
    A(A&&) { std::cout << "A(A&&)" << std::endl; }
};


int main() {
    A a(1);     // direct initialisation
    A b{1};     // direct initialisation
    A c = 1;    // copy initialisation
    A d = (1);  // copy initialisation
    A e = {1};  // copy initialisation
}
Run Code Online (Sandbox Code Playgroud)

编译程序复制省略禁用并运行它:

$ clang++ -std=c++14 -fno-elide-constructors main.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

A(int)
A(int)
A(int)
A(A&&)
A(int)
A(A&&)
A(int)
Run Code Online (Sandbox Code Playgroud)

为什么带大括号 ( A e = {1};) 的复制初始化会省略复制/移动构造(即使禁用复制省略)?


* 这种比较背后的动机是为了了解return expression自 C++ 11 以来函数返回语句 ( ) 的工作原理。当按值返回时,可以使用函数返回值的直接初始化或复制初始化。后者比像这里这样的变量的复制初始化更复杂,因为初始化由 表示的对象的函数返回值expression涉及首先尝试调用函数返回类型的移动构造函数(即使expression是左值),然后再回到它的复制构造函数。而且,由于C ++ 17,即复制/移动建设是保证,如果被省略的expression是prvalue(强制返回值优化),而它可能如果被省略expression是一个泛左(可选的命名返回值优化)。

Nic*_*las 6

用大括号复制初始化

哪有这回事。如果您使用花括号初始化列表来初始化一个对象,您正在执行某种形式的列表初始化。这有两种形式:复制列表初始化和直接列表初始化。在 C++14 中,这些与复制初始化和直接初始化无关(从技术上讲,直接列表初始化是直接初始化的一种语法形式,但由于列表初始化绕过了直接初始化所做的一切,更容易说直接列表初始化是它自己的野兽)。

列表初始化作为一个概念初始化一个对象。使用的Typename t{}是直接列表初始化,而使用的Typename t = {}是复制列表初始化。但无论涉及哪种形式,都不会创建临时;列表初始化初始化有问题的对象。您的示例中唯一的对象是e,因此这是被初始化的对象。

根据C++14 的列表初始化规则e通过调用构造函数来初始化,传递给它的值1是花括号初始化列表中的唯一值。