在此示例中,赋值运算符重载解析如何工作?结果对我来说意外

Kni*_*chi 14 c++ operator-overloading overload-resolution

这是我不理解的代码:

class Base
{
public:
    Base(){}

    Base operator=(Base ob2)
    {
        std::cout << "Using Base operator=() " << '\n';
        return *this;
    }
};

class Derived : public Base
{
public:
    Derived(){}
    Derived operator=(Base ob2)
    {
        std::cout << "Using Derived operator=() " << '\n';
        return *this;
    }
};

int main()
{
    Derived derived1, derived2;
    Base base1;

    derived1 = derived2;  // Uses base operator=()

    derived1 = base1;  // Uses derived operator=()

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

确定的第一个赋值使用Base类的运算符,第二个赋值使用Derived类的运算符的语言规则是什么?

是的,我知道通常不会像这样声明赋值运算符.这就是为什么我称它为accademical.

T.C*_*.C. 16

简短版:没有选择重载分辨率Base::operator=(Base).它选择了隐式声明的Derived::operator=(const Derived &),它调用Base::operator=(Base)copy-assign基类子对象.

带标准报价的长版:

首先,复制赋值运算符在§12.8[class.copy]/p17中的标准中定义:

甲用户声明的复制赋值运算符X::operator=是类X的类型中的正好一个参数的非静态的非模板的成员函数X,X&,const X&,volatile X&const volatile X&.

其次,如果您不提供复制赋值运算符,将始终为您隐式声明一个.来自§12.8[class.copy]/p18:

如果类定义未显式声明复制赋值运算符,则会隐式声明一个.如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为已删除; 否则,它被定义为默认值(8.4).如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用后一种情况.类X的隐式声明的复制赋值运算符将具有该表单

X& X::operator=(const X&) 
Run Code Online (Sandbox Code Playgroud)

如果

  • 每个直接基类BX具有复制赋值运算符,其参数的类型的const B&,const volatile B&或者B,与
  • 对于X类型为M(或其数组)的所有非静态数据成员,每个这样的类类型都有一个复制赋值运算符,其参数类型为const M&,const volatile M&M.

否则,隐式声明的复制赋值运算符将具有该表单

X& X::operator=(X&)
Run Code Online (Sandbox Code Playgroud)

请注意,这些规则的结果之一是(§12.8[class.copy]/p24):

因为如果未由用户声明,则为类隐式声明复制/移动赋值运算符,则基类复制/移动赋值运算符始终由派生类的相应赋值运算符隐藏.

换句话说,重载决策永远不能Base为从一个Derived到另一个的赋值选择复制赋值运算符.它始终是隐藏的,甚至不在候选函数集中.

最后,§12.8[class.copy]/p28提供了这一点

非联合类X的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动分配.

在问题的情况下,没有提供复制赋值运算符Derived,因此将隐式声明一个默认值(因为Derived没有用户声明的移动构造函数或移动赋值运算符).此隐式复制赋值运算符将通过重载决策选择,并执行基类子对象的复制赋值,该对象调用您为其定义的复制赋值运算符Base.