如果一个类拥有非静态const成员或者引用成员,那么其不应该支持拷贝赋值操作。

首先,需要明确的是,如果一个类有非静态的const成员、引用成员,那么编译器合成的拷贝赋值函数是deleted. 看以下这个例子:

class CT
{
public:
    CT(int a) : x(a) {}
private:
    const int x;
};

int main(int argc, char *argv[])
{
    CT c(5);
    CT cc = c;
    c = cc;
    return 0;
}

在main函数中首先做了直接初始化,然后是拷贝初始化——均没有问题。

但是到拷贝赋值时,编译报错:

error: use of deleted function 'CT& CT::operator=(const CT&)'
note: 'CT& CT::operator=(const CT&)' is implicitly deleted because the default definition would be ill-formed: class CT

error: non-static const member 'const int CT::x', can't use default assignment operator

即拷贝赋值被隐式删除了。这是因为: non-static const member ‘const int CT::x’, can’t use default assignment operator。

同理对有引用成员的类做拷贝赋值,会得到:

error: non-static reference member 'int& T::a', can't use default assignment operator

我们当然可以重写拷贝赋值函数,但是重写时要想保证编译正确且含义正确,其是不可能的!因为对const成员只能初始化,不能赋值;对引用变量赋值,改变的不是引用的指向,而是引用指向的变量的值。

所以,我们不应该让其支持拷贝赋值运算——要么我们不定义这样的类(通常来说意义不大——const是否必要?是否可以用static修饰?引用是否应该改为指针更加灵活?),要么我们显示将其定义为删除的。

最后,另外需要注意的相关点:

  1. 调用容器的push*操作一般会调用拷贝赋值(或移动赋值),这使得包含non-static const和引用的类成员不能直接放入到容器中。

  2. 拷贝赋值函数允许直接访问另一对象的private、protected成员。这正如初始化static成员一样,看起来似乎可以在类外访问private、protected成员。