【发布时间】:2020-08-03 10:14:32
【问题描述】:
这是一个 C++ 14 程序,用于比较 direct initialisation(没有 =)和 copy initialisation(=):*
#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
}
编译程序禁用copy elision并运行它:
$ clang++ -std=c++14 -fno-elide-constructors main.cpp && ./a.out
产生以下输出:
A(int)
A(int)
A(int)
A(A&&)
A(int)
A(A&&)
A(int)
为什么使用大括号 (A e = {1};) 进行复制初始化会忽略复制/移动构造(即使禁用了复制省略)?
* 此比较背后的动机是了解自 C++ 11 以来函数返回语句 (return expression) 的工作原理。按值返回时,可以使用函数返回值的直接初始化或复制初始化。后者比像这里这样的变量的复制初始化更复杂,因为从expression 表示的对象初始化函数返回值涉及trying to call the move constructor of the function return type first(即使expression 是左值),然后返回到其复制构造函数。从 C++ 17 开始,如果 expression 是纯右值(强制 return value optimisation),则保证复制/移动构造 被省略,而如果 @ 则 可能 987654338@ 是一个左值(可选named return value optimisation)。
【问题讨论】:
-
case c 和 e 从一开始就没有涉及到复制构造函数。
-
@jamesdlin:
c在 C++14 中确实如此,尽管几乎可以肯定它总是被省略。从不是T的对象复制初始化T需要通过转换创建T类型的临时对象,然后使用该临时对象来初始化c。 C++17 的保证省略规则使“临时”成为T类型的纯右值,从而跳过从创建的纯右值到正在初始化的对象的任何复制/移动。 -
我认为除了检查显式构造函数之外,
T x = {y};与T x{y};相同是正确的吗? -
@NicolBolas "从不是
T的对象复制初始化T需要通过转换创建T类型的临时对象,然后使用该临时对象进行初始化c" 我猜你的意思是通过 implicit 转换,而A a = A(1);将是通过 explicit 转换创建一个临时对象?
标签: c++ initialization c++14 language-lawyer copy-elision