繁体   English   中英

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

[英]How does the assignment operator overload resolution work in this example? The result is unexpected for me

这是我不理解的代码:

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;
}

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

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

短版本 :重载分辨率没有选择Base::operator=(Base) 它选择了隐式声明的Derived::operator=(const Derived &) ,它调用Base::operator=(Base)来复制 - 赋值基类子对象。

带标准报价的长版

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

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

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

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

 X& X::operator=(const X&) 

如果

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

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

 X& X::operator=(X&) 

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

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

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

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

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

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

创建类时,编译器会隐式生成以下函数(除非您明确指定其中一些函数,请参阅http://en.wikipedia.org/wiki/Special_member_functions ):

  • 默认构造函数
  • 复制构造函数
  • 移动构造函数(从c ++ 11开始)
  • 复制赋值运算符
  • 移动赋值运算符(自c ++ 11起)
  • 析构函数

在您的情况下,复制赋值操作员签名是:

struct Foo {
   Foo &operator=( const Foo &f ); // either this
   Foo &operator=( Foo f ); // does not make much sense but will work too
};

Derived类创建赋值运算符时,不会显式替换隐式副本赋值运算符,而是创建一个新运算符。 要更轻松地了解此问题,请将代码修改为:

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

问题应该变得明显。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM