以下 3 种定义对象的方法是否相同?

Mon*_*mer 5 c++ construction rvalue-reference language-lawyer c++11

根据我的理解,以下内容是相同的:

Person p{}; // Case 1
Person p = {}; // Case 1.5
Run Code Online (Sandbox Code Playgroud)

我注意到

Person p = Person{}; // Case 2
Run Code Online (Sandbox Code Playgroud)

产生Case 1Case 1.5上述及以上相同的跟踪输出。

  • 问题 1:将案例 2 与案例 1 或案例 1.5 进行比较,是因为复制省略还是其他原因?

  • 问题2:以下有什么区别?

Person p{};            // Case 1
Person p = Person{};   // Case 2
Person&& p = Person{}; // Case 3
Run Code Online (Sandbox Code Playgroud)

Hum*_*ler 4

中并不完全相同。

情况 2 需要 C++17 之前的移动构造

该语言要求代码存在移动构造函数X x = X{}——否则代码将无法编译。

例如,使用Person如下定义的类:

class Person{
public:
    ...
    Person(Person&&) = delete;
    ...
};
Run Code Online (Sandbox Code Playgroud)

将无法编译如下语句:

Person p = Person{}; // Case 2
Run Code Online (Sandbox Code Playgroud)

编译器资源管理器上的示例

注意:上面的代码在及更高版本中是完全有效的,因为措辞发生了变化,即使对象不可移动且不可复制,也可以直接在其目标地址中构造对象(这就是人们通常所说的“保证复制省略”) ”)。

情况3是临时的生命周期延长

第三种情况是临时构造,其生命周期通过绑定到右值引用来延长。在某些情况下,临时对象绑定到右值引用或const左值引用,可以延长它们的生命周期。例如,以下两个构造在绑定到临时生命周期方面是等效的:

Person&& p3_1 = Person{};
const Person& p3_2 = Person{};
Run Code Online (Sandbox Code Playgroud)

就作用域规则而言,它与任何其他自动变量具有相同的生命周期(例如,它将以与将要使用的方式相同的方式在作用域末尾调用析构函数Person person{})。然而,至少在中,构造可以完成的工作与此代码完全不同,即使不存在移动构造函数,此代码也将始终编译(因为这是引用绑定)。Person p2 = Person{}

例如,让我们考虑一个不可移动、不可复制的类型,例如std::mutex. 在C++17中,编写以下代码是有效的:

std::mutex mutex = std::mutex{};
Run Code Online (Sandbox Code Playgroud)

但在C++11中却无法编译。但是,您可以自由地写:

std::mutex&& mutex = std::mutex{};
Run Code Online (Sandbox Code Playgroud)

它创建一个临时变量并将其绑定到一个引用,该引用的生命周期将与此时构造的任何作用域变量相同。

编译器资源管理器上的示例。

注意:故意传播临时对象的生命周期通常不是有意做的事情,但在 C++17 之前,这是使用不可移动对象实现几乎总是自动语法的唯一方法。例如,上面的内容可以重写:auto&& mutex = std::mutex{}