复制构造函数对临时对象的调用

4

我有以下代码:

#include <iostream>
using namespace std;
struct A
{
    A() {}
    A(const A&)  { cout << "copy const" << endl; }
    A(A&)  { cout << "copy non const" << endl; }
};
A f(A a)
{
    return a;
}
int main() {
  A a1 = f(A());
}

被调用的是A(A&)复制构造函数。为什么传递临时对象时没有调用A(const A&)复制构造函数? 如果我注释掉A(const A&)复制构造函数,则程序无法编译。

使用 gcc 9.2 的实时示例:https://godbolt.org/z/585J59 - François Andrieux
1个回答

8
你看到的是复制省略和实际复制的混合体。由于f按值获取a,因此需要将A()复制到a中。编译器看到这个副本实际上不需要,所以它省略了这个步骤,并直接构造a,因此您看不到任何调用。在f的主体中,当您return a;时,需要将a复制到返回值中。由于a是一个左值,A(A&)A(const A&)更匹配,因此您会看到非const复制构造函数的调用。然后需要从f的返回值初始化a1。再次使用复制省略来避免看到复制,它只是直接将返回值放入a1的存储中。
因此,省略、非const复制、省略,这样输出就只有复制非const
当您删除A(const A&)时,会出现错误,因为尽管这些副本已被省略,但在C++17之前,C++仍需要存在复制构造函数。
如果您使用gcc或clang编译,并使用-fno-elide-constructors,您实际上可以看到这些副本。您可以在此实时示例中查看。请注意,我使用了-std=c++11关闭了C++17的拷贝保证消除

不对,我记错了。实际上是当我把它们全部注释掉时,编译器隐式地创建了一个对象,然后省略了它。 - NathanOliver
非常好的回答!“non const copy constructor” - 从技术上讲,这样的东西并不存在。这是一个转换构造函数,看起来类似于复制构造函数。 - Mikhail
@Mikhail 实际上有 https://timsong-cpp.github.io/cppwp/class.copy.ctor#3。`T(T&)`T(const T&) 都是复制构造函数。 - NathanOliver
@Timo RVO保证了本地lvalue的功能。即使named是一个lvalue,A foo() { A named; return named; }也会进行RVO。 - NathanOliver
@NathanOliver 这个确实有保证吗?cppreference 没有提到,但我猜它们不是标准。 - Timo
显示剩余14条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接