C++ 复制构造函数导致代码无法编译 (gcc)

标签 c++ constructor reference copy constants

我有以下无法编译的代码。编译错误是:

"error: no matching function to call B::B(B)",
candidates are B::B(B&) B::B(int)"

代码在以下两个条件之一下编译:

  1. 取消注释函数 B(const B&)
  2. 将“主要”更改为以下内容

    int main()
    {
            A a;
            B b0;
            B b1 = b0;
            return 0;
    }
    

如果我执行第 1 步,代码会编译,但从输出中可以看出它正在调用“非 const 复制构造函数”。

谁能告诉我这是怎么回事?

using namespace std;
class B
{
    public:
    int k;
     B()
     { 
        cout<<"B()"<<endl; 
     }
     B(int k) 
     { 
        cout<<"B(int)"<<endl;
        this->k = k;
     }
     /*B(const B& rhs) { 
        cout<<"Copy constructor"<<endl;
        k = rhs.k;
     }*/
     B(B& rhs) 
     { 
        cout<<"non const Copy constructor"<<endl;
        k = rhs.k;
     }
     B operator=(B& rhs)
     {
        cout<<"assign operator"<<endl;
        k = rhs.k;
        return *this;
     }  
};

class A
{
    public:
        B get_a(void)
        {
            B* a = new B(10);
            return *a;
        }       
};

int main()
{
    A a;
    B b0 = a.get_a();  // was a.just();
    B b1 = b0 ;
    return 0;
}

最佳答案

我对此做了一些额外的阅读,正如我一直怀疑的那样,发生这种情况的原因是由于返回值优化。作为Wikipedia article解释说,RVO 是允许编译器在分配临时对象或将它们复制到永久变量的过程中消除临时对象的允许机制。此外,RVO 是允许违反 as-if 规则的少数几个(如果不是唯一的话)功能之一,根据该规则,编译器只有在具有相同的可观察行为时才被允许进行优化 < em>好像优化从来没有在一开始就进行过——这是解释这里行为的关键的豁免(我承认我只是在研究这个问题时才知道这个豁免,这就是为什么我也是一开始很困惑)。

在您的情况下,GCC 足够聪明,可以避免两个拷贝之一。将您的代码归结为一个更简单的示例

B returnB()
{
    B a;
    B* b = &a;
    return *b;
}

int main()
{
    B c = returnB();
    return 0;
}

如果遵循标准,不进行RVO,则在制作c的过程中制作了两份——将*b复制到returnB 的返回值,以及返回值的拷贝到 c 本身。在你的例子中,GCC 省略了第一个拷贝,而是只制作了一个拷贝,从 *b 直接到 c。这也解释了为什么调用 B(B&) 而不是 B(const B&) —— 因为 *b (又名 a) 不是临时值,编译器不再需要使用 B(const B&) 而是选择更简单的 B(B&) 调用构造 c(如果存在选择,非 const 重载始终优先于 const 重载)。

那么,如果 B(const B&) 不存在,为什么编译器仍然报错?那是因为您的代码语法必须正确才能进行优化(如 RVO)。在上面的示例中,returnB() 返回一个临时的(根据 C++ 语法规则),因此编译器必须看到一个 B(const B&) 复制构造函数。但是,一旦编译器确认您的代码在语法上是正确的,它就可以进行优化,使 B(const B&) 永远不会被使用。

编辑:向在 C++ 标准中发现以下内容的 Charles Bailey 致敬

12.2 [class.temporary]: "Even when the creation of the temporary is avoided, all the semantic restrictions must be respected as if the temporary object was created."

这只是强化并确认了在复制临时对象进行构造时需要一个引用 const 的复制构造函数(无论是否实际使用构造函数)

关于C++ 复制构造函数导致代码无法编译 (gcc),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1517703/

相关文章:

c++ - _BitScanForward _BitScanForward64 缺失 (VS2017) Snappy

c++ - 如何使用istringstream提取混合格式

c++ - 在 if 语句中处理多个或的更优雅的方法是什么

javascript - 如何使 Node.js MongoDB 遵循对象引用?

C++ 队列实现错误

实例化时未调用 PHP 构造函数

c# - 新的运算符或构造函数是否会阻止.NET中的其他线程?

java - 无默认构造函数 - 继承

java - 如何在java中返回多个整数?

c++ - "There is no “reference-to-member” type in C++", std::bind 和 boost::bind 在同一条船上