如果 C++ class 同时包含 const 引用和非 const 引用复制构造函数怎么办?

what if C++ class contains both const reference and non-const reference copy constructor?

片段 1:

#include<iostream>
using namespace std;

class C{
public:
    C(){}
    C(const C& c){
        cout<<"const copy constructor called"<<endl;
    }
};
int main(){
    C c1;
    C c2 = c1;
    return 0;
}

输出:const拷贝构造函数调用


片段 2:

#include<iostream>
using namespace std;

class C{
public:
    C(){}
    C(const C& c){
        cout<<"const copy constructor called"<<endl;
    }
    C(C& c){
        cout<<"non-const copy constructor called.\t "<<endl;
    }
};
int main(){
    C c1;
    C c2 = c1;
    return 0;
}

输出:调用了非常量复制构造函数


片段 3:

#include<iostream>
using namespace std;

class C{
public:
    C(){}
    C(const C& c){
        cout<<"const copy constructor called"<<endl;
    }
    C(C c){
        cout<<"non-const copy constructor called.\t "<<endl;
    }
};
int main(){
    C c1;
    C c2 = c1;
    return 0;
}

输出:错误:复制构造函数必须通过引用传递它的第一个参数


我很困惑:

  1. 对于片段 2,为什么这里的非 const 复制构造函数有效?为什么调用非 const 复制构造函数,而不是 const 复制构造函数。
  2. 对于片段 3,我知道复制构造函数必须使用 const 引用来避免无限递归。但是这里class C已经得到了C(const C& c)C(C c)不会造成无限递归,为什么还是不行?

片段 1:一个带有 const T& 的标准复制构造函数。快乐的世界。

片段 2:

您有效地完成了复制构造函数的重载 - 一个采用引用 T&,另一个采用常量引用 const T&

请注意:class T 的任何构造函数具有一个 T &const T & 类型的强制参数(它也可能有更多的默认参数)是一个复制构造函数.

因此,对于编译器而言,这一切都归结为找到 最适合 的重载解析,其完成方式如下:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:

  • ....
  • S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

所以写

C c1;
C c2 = c1;

将调用非常量复制构造函数,因为它更匹配,但是,

写作,

const C c1;
C c2 = c1;

将调用 const 复制构造函数(您可以检查),因为现在带有 const 的复制构造函数是唯一可行的匹配项。

片段 3 对于编译器来说完全是错误的。

C(C c){
        cout<<"non-const copy constructor called.\t "<<endl;
    }

您不能拥有带有签名的方法 C(C c)。编译器认为您正在尝试编写复制构造函数而错过了 & 的编写,因此报告了错误。删除它,它工作正常。

@除非你有很好的理由,否则 永远不要使用 C(C& c) 作为你的复制构造函数。不要跳过 const,因为改变您从中制作副本的对象没有多大意义。

for snippet 2, why the non-const copy constructor here is valid? why non-const copy constructor was called, rather than the const one.

考虑您针对此问题的代码,但在下面进行更改并添加注释 // (*):

int main(){
    const C c1; // (*) <- See change here
    C c2 = c1;
    return 0;
}

这调用了const copy ctor 版本。它真的与碰巧是构造函数的函数无关——如果一个函数有两个重载,一个接受引用,一个接受 const 引用,那么非常量对象将被第一个调用,并且const 个对象。

for snippet 3, I know that copy constructor must use const reference to avoid infinite recursion. But Here class C has got C(const C& c), C(C c) won't cause infinite recursion, why it still doesn't work?

考虑以下代码,并注意没有调用正在进行(main 的内容几乎被删除)。

#include<iostream>
using namespace std;

class C{
public:
    C(){}
    C(const C& c){
        cout<<"const copy constructor called"<<endl;
    }
    C(C c){
        cout<<"non-const copy constructor called.\t "<<endl;
    }
};
int main(){
// Note that nothing is creating C instances at all.
return 0;
}

这会导致完全相同的错误 - 编译器只是拒绝使用此接口编译 class,无论是否有东西试图调用它。

引用 this question 的回答“它被 §12.8/3 中的标准禁止:

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv- qualified) X and either there are no other parameters or else all other parameters have default arguments.

"