Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Here is the code that I do not understand:

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

What are the language rules that determine, that the first assignment uses the operator of the Base class and the second the operator of the Derived class?

Yes and I know that one normally does not declare an assignment operator like this. That is why I called it accademical.

like image 654
Knitschi Avatar asked Jul 16 '14 19:07

Knitschi


People also ask

How do you overload an assignment operator?

Overloading the assignment operator (operator=) is fairly straightforward, with one specific caveat that we'll get to. The assignment operator must be overloaded as a member function. This will call f1. operator=(f1), and under the simplistic implementation above, all of the members will be assigned to themselves.

Why assignment operator is already overloaded?

The compiler generates the function to overload the assignment operator if the function is not written in the class. The overloading assignment operator can be used to create an object just like the copy constructor.

What is overload resolution?

The process of selecting the most appropriate overloaded function or operator is called overload resolution. Suppose that f is an overloaded function name. When you call the overloaded function f() , the compiler creates a set of candidate functions.

Can a friend function be used to overload the assignment operator?

A friend function is a non-member function of the class to which it has been defined as friend. Therefore it just uses the functionality (functions and data) of the class. So it does not consist the implementation for that class. That's why it cannot be used to overload the assignment operator.


1 Answers

Short version: Overload resolution didn't select Base::operator=(Base). It selected the implicitly declared Derived::operator=(const Derived &), which calls Base::operator=(Base) to copy-assign the base-class subobject.

Long version with standard quotes:

First, copy assignment operator is defined in the standard in §12.8 [class.copy]/p17:

A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.

Second, if you don't provide a copy assignment operator, one will always be implicitly declared for you. From §12.8 [class.copy]/p18:

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor. The implicitly-declared copy assignment operator for a class X will have the form

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

if

  • each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or B, and
  • for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy assignment operator whose parameter is of type const M&, const volatile M& or M.

Otherwise, the implicitly-declared copy assignment operator will have the form

X& X::operator=(X&)

Note that one of the results of these rules is that (§12.8 [class.copy]/p24):

Because a copy/move assignment operator is implicitly declared for a class if not declared by the user, a base class copy/move assignment operator is always hidden by the corresponding assignment operator of a derived class.

In other words, overload resolution can never select the copy assignment operator of Base for an assignment from one Derived to another. It is always hidden and isn't even in the set of candidate functions.

Finally, §12.8 [class.copy]/p28 provides that

The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy-/move assignment of its subobjects.

In the case in the question, no copy assignment operator is provided for Derived, so one will be implicitly declared as defaulted (since Derived has no user-declared move constructor or move assignment operator). This implicit copy assignment operator will be selected by overload resolution, and performs copy assignment of, among other things, the base class subobject, which calls the copy assignment operator you defined for Base.

like image 50
T.C. Avatar answered Sep 18 '22 16:09

T.C.