为什么我不能有一个纯虚拟赋值运算符?

Why can't have I a pure virtual assignment operator?

我有点迷失在 C++ 运算符中。我想为两个不同的 类 强制执行赋值运算符,即这样一个人可以相互赋值:

class A {
public:
    virtual A &operator =(const A &a) = 0;
};

class B : public A {
public:
    virtual A &operator =(const A &a) override {
        std::cout << "B" << std::endl;
        return *this;
    }
};

class C : public A {
public:
    virtual A &operator =(const A &a) override {
        std::cout << "C" << std::endl;
        return *this;
    }
};

int main(int argc, char *argv[])
{
    B b;
    C c;
    b = c;

    // leads to a linker error: undefined reference to `A::operator=(A const&)'
    //B b2;
    //b = b2;
}

第一个任务似乎完成了任务,"B" 被调用了。同样,对于 "c = b",会调用 "C"。但是,当我取消注释第二部分时,出现链接器错误。如果我像这样定义 A 的运算符:

virtual A &operator =(const A &a) {
        std::cout << "A" << std::endl;
        return *this;
} 

我得到 "B"、"A"。嗯?有人可以解释为什么在分配两个 B 时需要 "A" 而在分配 B <- C 时不需要吗?

编译器生成一个隐式复制赋值运算符,当您执行 B = B 赋值时,该运算符将被选中。当您执行 B = C 分配时,不会选择它。

http://en.cppreference.com/w/cpp/language/copy_assignment

https://wandbox.org/permlink/CM5tQU656rnwtrKl

如果您查看错误消息:

/tmp/cctHhd0D.o: In function `B::operator=(B const&)':
prog.cc:(.text._ZN1BaSERKS_[_ZN1BaSERKS_]+0x1f): undefined reference to `A::operator=(A const&)'
collect2: error: ld returned 1 exit status

您可以看到链接器错误来自 B::operator=(B const&) 内部,由于您没有定义,这意味着它必须是自动生成的。

当您分配 b = b2; 时,它会尝试调用 B 的默认(隐式)分配。 而默认赋值会调用基class的默认赋值,所以最终会调用A::operator=(const A &a),也就是纯虚

所以你得到 link 错误。

根据标准,在派生 class 中覆盖基 class 的虚拟赋值运算符不会阻止生成在您的情况下调用的默认复制赋值运算符。 class B 的默认复制赋值运算符将直接调用 class A 的复制赋值运算符,因此您会收到 undefined reference 错误。

13.5.3 Assignment [over.ass]

2 Any assignment operator, even the copy and move assignment operators, can be virtual. [Note: For a derived class D with a base class B for which a virtual copy/move assignment has been declared, the copy/move assignment operator in D does not override B’s virtual copy/move assignment operator. [Example:

struct B {
    virtual int operator= (int);
    virtual B& operator= (const B&);
};

struct D : B {
    virtual int operator= (int);
    virtual D& operator= (const B&);
};

D dobj1;
D dobj2;
B* bptr = &dobj1;

void f()
{
    bptr->operator=(99); // calls D::operator=(int)
    *bptr = 99; // ditto
    bptr->operator=(dobj2); // calls D::operator=(const B&)
    *bptr = dobj2; // ditto
    dobj1 = dobj2; // calls implicitly-declared D::operator=(const D&)
}