5

据我了解,以下是相同的:

Person p{}; // Case 1
Person p = {}; // Case 1.5

我注意到

Person p = Person{}; // Case 2

Case 1产生与上述相同的跟踪输出Case 1.5

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

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

Person p{};            // Case 1
Person p = Person{};   // Case 2
Person&& p = Person{}; // Case 3
4

2 回答 2

4

中并不完全相同。

案例 2 需要 C++17 之前的移动构造

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

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

class Person{
public:
    ...
    Person(Person&&) = delete;
    ...
};

将无法编译以下语句:

Person p = Person{}; // Case 2

编译器资源管理器示例

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

案例 3 是临时的终身延长

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

Person&& p3_1 = Person{};
const Person& p3_2 = Person{};

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

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

std::mutex mutex = std::mutex{};

但是在C++11中编译失败。但是,您可以自由编写:

std::mutex&& mutex = std::mutex{};

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

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

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

于 2020-10-01T12:35:27.537 回答
1

的 - 关于构造将如何发生,以及构造变量的行为方式;但就变量的类型而言,否。

编译器在任何这些情况下都不使用赋值,即它只有您的程序默认构造。您可以使用此代码来验证:

#include <iostream>

struct Person {
    Person& operator=(Person&) {
       std::cout << "Assignment: operator=(Person&)\n";  return *this; 
    }
    Person& operator=(Person&&) { 
       std::cout << "Move assignment: operator=(Person&&)\n";  return *this; 
    }
    Person(const Person&) { std::cout << "Copy ctor: Person(Person&)\n"; }
    Person(Person&&) { std::cout << "Move ctor: Person(Person&&)\n"; }
    Person() { std::cout << "Default ctor: Person()\n"; }
};

int main() {
    std::cout << "P1:\n";
    Person p1{};
    std::cout << "Address of P1: " << &p1 << '\n';
    std::cout << "P2:\n";
    Person p2 = Person{};
    std::cout << "Address of P2: " << &p2 << '\n';
    std::cout << "P3:\n";
    Person&& p3 = Person{};
    std::cout << "Address of P3: " << &p3 << '\n';
}

看到它GodBolt

第三个语句的行为让我有点惊讶。我实际上虽然编译器可能会直接拒绝它。无论如何 -请不要像那样声明右值引用。这让读者感到困惑——即使对我来说,也几乎可以肯定不是你想做的事。我确信它的p3行为就像一个右值引用;但是 - 显然情况并非如此:尽管有 type Person&&,但它在传递给函数时会表现得像左值引用。

于 2020-09-27T17:28:33.067 回答