0

Implementing a derived class from abstract base class with assignment operator by using dynamic cast in base-to-derived assignment operator, I'd like to call derived-to-derived assignment operator. This works.

#include <iostream>
using namespace std;

class base

{
public:
    virtual base& operator = (const base& ) = 0;
};

class derived: public base
{
public:
    derived (int d): data(d) {};
    derived& operator = (const base& b)
    {
        if (&b == this) return *this;
        const derived &d = dynamic_cast<const derived&> (b);
        *this = d;
        return *this;
    }
    derived& operator = (const derived& d)
    {
        if (&d == this) return *this;
        data = d.data;
        return *this;
    }
private:
    int data;
};

However when I do not implement derived& operator = (const derived& d) explicitly or use

    derived& operator = (const derived& d) = default;

compilation fails, complaining 'undefined reference to base::operator=(base const&)' (i.e. trying to call abstract method). Why? Is there a way not to implement default assignment? This seems to be a redundant and may result in future errors, e.g. in case of adding a field into base/derived class without corresponding modifying the assignment operator?

Nick
  • 970
  • 10
  • 20
  • [Adding some debugging output](https://godbolt.org/z/xWobnhMs9) might perhaps add some insight? – Some programmer dude Oct 28 '22 at 13:04
  • @Someprogrammerdude `/usr/bin/ld: /tmp/ccRIsldA.o: in function 'derived::operator=(derived const&)': a.cpp:(.text._ZN7derivedaSERKS_[_ZN7derivedaSERKS_]+0x1f): undefined reference to 'base::operator=(base const&)' collect2: error: ld returned 1 exit status` The difference is that I have an abstract `base::operator =`. – Nick Oct 28 '22 at 15:54
  • Well, in your example when `derived::operator=(const derived&)` is defined we have what is expected (`derived::operator=`) is always called, but `default` version gives case 1 `base::operator=(const base&)` case 2 `derived::operator=(const base&) base::operator=(const base&)` case 3 `derived::operator=(const base&) base::operator=(const base&)` case 4 `derived::operator=(const base&) base::operator=(const base&)` which is very weird especially for case 1 which is derived to derived assignment. – Nick Oct 28 '22 at 15:57
  • 1
    But finally I see the reason, thank you. Default derived assignment first call base assignment, this is how c++ build default operator, this is the answer to the question. – Nick Oct 28 '22 at 16:00
  • Since you figured it out, please write an actual answer that you can accept (and that others can up-vote). :) – Some programmer dude Oct 28 '22 at 19:41

1 Answers1

2

If you want to force derived classes D to implement the function D::operator=(const base&) and you also want them to be able to have their own defaulted copy-assignment operators, D::operator=(const D&) = default;, then:

  • make base::operator=(const base&) pure (as you have already done), and
  • provide an out-of-line definition of base::operator=(const base&). This looks like: base& base::operator= (const base&) = default; The definition of base's copy-assignment operator is required since it will be called by the defaulted copy-assignment operator of each derived class.

It's surprising, but a function that is declared pure can still provided with a definition.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Thank you a lot, that's really surprising. So we need to have `base& operator= (const base&) = 0` inside the class and `base& base::operator= (const base&) = default` outside the class. What general rule does this follow? So I can have any definition of pure virtual, not only default? Why can't I just have `base& operator= (const base&) = default` inside the class, it complains at redefinition in derived for loosing constexpr specificator? – Nick Oct 31 '22 at 08:37
  • @Nick If you don't need it to be pure virtual, then just define it in the class definition. If you need it to be pure virtual, you can also supply an out of line definition as I showed (it can be `= default;` or anything else). The out of line definition is required in cases where the function will be implicitly invoked by defaulted functions in derived classes or by derived class destructors. – Brian Bi Oct 31 '22 at 12:03