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

时间:2016-02-22 05:26:39

标签: c++ copy-constructor const-reference

摘录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. for snippet 2,为什么这里的非const复制构造函数有效?为什么调用非const复制构造函数而不是const函数。
  2. for snippet 3,我知道复制构造函数必须使用const引用来避免无限递归。但是,C类已经C(const C& c)C(C c)不会导致无限递归,为什么它仍然不起作用?

2 个答案:

答案 0 :(得分:8)

Snippet 1 :一个标T&的标准副本构造函数。快乐的世界。

代码段2

您有效完成的工作是重载复制构造函数 - 一个引用引用const T&而另一个引用常量引用T &

请注意:类T的任何构造函数都有一个类型为const T &C c1; C c2 = c1; 的强制参数(它可能还有其他默认参数)是一个复制构造函数。

所以,对于编译器来说,这一切只是归结为找到最适合来进行重载解析,它完成如下:

  

标准转换序列S1是比转换序列更好的转换序列   标准转换序列S2 if:

     
      
  • ....
  •   
  • S1和S2是引用绑定(8.5.3),引用引用的类型除了顶级cv限定符之外是相同的类型,   并且S2引用的引用所引用的类型更多   cv-qualified比S1初始化的引用类型   指。
  •   

所以写作

const C c1;
C c2 = c1;

将调用非const复制构造函数,因为它是更好的匹配,但是,

写入,

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

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

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

C(C c)

您无法使用签名&的方法。编译器认为您正在尝试编写复制构造函数并错过编写C(C& c)并因此报告错误。删除它,它工作正常。

@除非你有充分的理由,从未使用const作为你的拷贝构造函数。不要跳过import org.acra.ReportingInteractionMode; import org.acra.annotation.ReportsCrashes; @ReportsCrashes(formUri = "", mode = ReportingInteractionMode.DIALOG, mailTo = "xyz@gmail.com", resDialogText = R.string.reporttous, resDialogOkToast = R.string.OK, formKey = "") public class UILApplication extends Application { @Override public void onCreate() { super.onCreate(); ACRA.init(this); } } ,因为改变你制作副本的对象并没有多大意义。

答案 1 :(得分:3)

  

for snippet 2,为什么这里的非const复制构造函数有效?为什么调用非const复制构造函数,而不是const。

请考虑此问题的代码,但请在下面添加评论// (*)

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

这会调用const复制版本。它实际上与作为构造函数的函数无关 - 如果函数有两个重载,一个接受引用,一个引用const,那么将使用第一个调用非const对象,并且第二个const个对象。

  

对于代码片段3,我知道复制构造函数必须使用const引用来避免无限递归。但是这里C类有C(const C&amp; c),C(C c)不会导致无限递归,为什么它仍然不起作用?

请考虑以下代码,并注意没有调用(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;
}

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

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

  

如果类X的构造函数的第一个参数是类型(可选择cv-qualified)X并且没有其他参数,或者所有其他参数都有默认参数,那么它的构造函数声明是错误的。