5

片段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 构造函数。
  2. 对于片段 3,我知道复制构造函数必须使用 const 引用以避免无限递归。但是 Here class C has got C(const C& c)C(C c)不会导致无限递归,为什么它仍然不起作用?
4

2 回答 2

8

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

片段 2

您实际上所做的是重载了复制构造函数 - 一个接受引用T&,另一个接受常量引用const T&

请注意:类 T 的任何构造函数,只要具有一个类型的强制参数T &const T &(它还可能具有更多的默认参数),都是复制构造函数。

因此,对于编译器来说,这一切都归结为找到最适合重载解决方案的方法,它的完成方式如下:

标准转换序列 S1 是比标准转换序列 S2 更好的转换序列,如果:

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

所以写

C c1;
C c2 = c1;

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

写作,

const C c1;
C c2 = c1;

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

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

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

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

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

于 2016-02-22T06:00:13.830 回答
3

对于代码段 2,为什么这里的非常量复制构造函数有效?为什么调用非 const 复制构造函数,而不是 const 构造函数。

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

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

这称为const复制 ctor 版本。它实际上与恰好是构造函数的函数无关 - 如果一个函数有两个重载,一个带引用,一个带const引用,那么非常量对象将与第一个一起调用,而const对象与第二个一起调用。

对于片段 3,我知道复制构造函数必须使用 const 引用以避免无限递归。但是这里的类 C 有 C(const C& 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;
}

这会导致完全相同的错误——编译器只是拒绝用这个接口编译一个类,不管是否有东西试图调用它。

引用这个问题的答案“第 12.8/3 节中的标准禁止这样做:

如果类 X 的第一个参数是类型(可选 cv 限定)X 并且没有其他参数或者所有其他参数都具有默认参数,则类 X 的构造函数声明是格式错误的。

"

于 2016-02-22T06:06:43.567 回答