为什么在这段代码中复制构造函数被调用了两次?

Why is the copy constructor called twice in this code snippet?

我正在研究一些东西以了解复制构造函数的工作原理。但是我无法理解为什么复制构造函数被调用两次来创建 x2。当 createX() 的 return 值被复制到 x2.
时,我会假设它会被调用一次 我还查看了一些关于 SO 的相关问题,但据我所知,我找不到与我在这里询问的相同的简单场景。

顺便说一句,我正在使用 -fno-elide-constructors 进行编译,以便在没有优化的情况下查看发生了什么。

#include <iostream>

struct X {
    int i{2};

    X() {
        std::cout << "default constructor called" << std::endl;
    }

    X(const X& other) {
        std::cout << "copy constructor called" << std::endl;
    }
};

X createX() {
    X x;
    std::cout << "created x on the stack" << std::endl;
    return x;
}

int main() {
    X x1;
    std::cout << "created x1" << std::endl;
    std::cout << "x1: " << x1.i << std::endl << std::endl;    

    X x2 = createX();
    std::cout << "created x2" << std::endl;
    std::cout << "x2: " << x2.i << std::endl;    

    return 0;
}

这是输出:

default constructor called
created x1
x1: 2

default constructor called
created x on the stack
copy constructor called
copy constructor called
created x2
x2: 2

有人可以帮助我解决我在这里遗漏或忽略的问题吗?

第一个副本在 createX

的 return 中
X createX() {
    X x;
    std::cout << "created x on the stack" << std::endl;
    return x; // First copy
}

第二个是通过 createX 从临时 return 创建 x2。

X x2 = createX(); // Second copy

请注意,在 C++17 中,第二个副本被强制删除。

这里你必须记住的是,函数的 return 值是一个不同的对象。当你这样做时

return x;

您使用 x 复制初始化 return 值对象。这是您看到的第一个复制构造函数调用。那么

X x2 = createX();

使用 returned 对象复制初始化 x2 所以这是您看到的第二个副本。


需要注意的一件事是

return x;
如果可以,

将尝试将 x 移动到 return 对象中。如果你做了一个移动构造函数,你会看到这个调用。这样做的原因是因为局部对象在函数末尾超出范围,编译器将对象视为右值,并且只有在找不到有效重载时才会回退到 returning 它作为左值。