【问题标题】:Why does copy initialisation with braces elide copy/move construction? [duplicate]为什么使用大括号进行复制初始化会忽略复制/移动构造? [复制]
【发布时间】: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


【解决方案1】:

用大括号复制初始化

没有这样的事情。如果你使用一个花括号初始化列表来初始化一个对象,你正在执行某种形式的列表初始化。这有两种形式:复制列表初始化和直接列表初始化。在 C++14 中,这些与复制初始化和直接初始化无关(从技术上讲,直接列表初始化是 grammatical form of direct-initialization,但由于 list-initialization bypasses everything that direct-initialization would have done,更容易说直接列表初始化是它的自己的野兽)。

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

根据C++14 rules for list-initializationecalling a constructor 初始化,并传递一个值1,这是braced-init-list 中的唯一值。

【讨论】:

  • direct-list-initialisation 和 copy-list-initialisation 都不会创建临时对象,这是否是大括号被称为 uniform initialisation syntax 的原因?
  • @Maggyero:不;它被认为是“统一初始化”,因为其想法是初始化规则在所有地方都是相同的,无论您如何使用 {},对于任何给定的 T 正在初始化和任何给定的参数集,将适用相同的初始化规则。显然,整个复制/直接列表初始化的区别使得它不起作用..
猜你喜欢
  • 2023-03-05
  • 1970-01-01
  • 2011-09-10
  • 2020-05-06
  • 2015-08-11
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
  • 2011-09-04
相关资源
最近更新 更多