3

I have a question about operator= that accepts parent reference type.

When there is a abstract class and it's implemented class, why is not enough to have single operator= that accept parent reference type?

Below is my code

#include <iostream>

class AbstractType {
    public:
        virtual ~AbstractType() {}
        virtual AbstractType& operator=(const AbstractType& other) {
            std::cout << "AbstractType& operator=(const AbstractType&)" << std::endl;
            return *this;
        }
};

class Type1: public AbstractType {
    public:
        virtual ~Type1() {}
        virtual Type1& operator=(const AbstractType& other) {
            std::cout << "Type1& operator=(const AbstractType&)" << 
std::endl;
            return *this;
        }

        /*
        virtual Type1& operator=(const Type1& other) {
            std::cout << "Type1& operator=(const Type1&)" << std::endl;
            // Just redirecting here! What a waste!
            return operator=(dynamic_cast<const AbstractType&>(other));
        }
        */
};

int main()
{
    Type1 t1;
    AbstractType& r1 = t1;

    Type1 t2;
    AbstractType& r2 = t2;


    // Type1& operator=(const AbstractType&). It's fine!
    r1 = r2;

    // AbstractType& operator=(const AbstractType&)!! Why??
    // Expected `Type1& operator=(const AbstractType&)` to be called!
    t1 = t2; 

    return 0;
}

You can find given parameter is being just redirected in Type1& operator=(const Type1&) that is ignored by comment.

Uncomment Type1& operator=(const Type1&) is just working for me, but if say, I have more than a hundred of TypeX then I have to make two hundred of copy assignment which is I can't understand because it seems to me just having Type1& operator=(const AbstractType& other) is enough.

And most cases I only have AbstractType to handle things around. Very rare to know it's specific type in advance under limited situations.

Anyone can suggest me better solution?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    Possible duplicate of [virtual assignment operator C++](https://stackoverflow.com/questions/669818/virtual-assignment-operator-c) – Acorn Jun 21 '18 at 15:21
  • As you cannot change the type of an existing object, making the assignment operator virtual does not make much sense. You increase the chance of introducing bugs in your code. Maybe you want to clone objects and create them dynamically. – Phil1970 Jun 21 '18 at 16:10
  • 1
    Assignment of polymorphic types rarely makes sense. Perhaps you need to manipulate (smart) pointers to polymorphic objects rather than objects themselves. – n. m. could be an AI Jun 21 '18 at 16:35
  • @Phil1970, @n.m. Thank you for the suggestions! But I have two lists of AbstractionType. Identical type will be ensured when access same index in each list, so they have same size. What I want to do is to make assignments during the loop. In pseudo code it will be `for i in 0 to length; *(dest[i]) = *(source[i])`. That is the reason for making virtual operator. Any suggestion? Thank you! – 0ctopus13prime Jun 22 '18 at 02:27
  • @0ctopus13prime If you do a loop, then show some code that show how you arrays are declared. We already told you that in most cases virtual assignment does not make any sense (to someone who understand the language) but in every anwser you seems to not really understand any given comment. **Your question miss details that would help us clearly understand what you are trying to achieve!** There areprobably many way to solve your problem **and virtual assignment is probably not one of them.** Stop trying to use the wrong tool for the job. If you want to duplicate an array, cloning is the answer. – Phil1970 Jun 23 '18 at 13:43
  • In reality, if type on both side can vary and cloning is not appropriate, then you need double dispatch. **You should be able to find that kind on information in some very ADVANCED C++ books**. – Phil1970 Jun 23 '18 at 14:00

2 Answers2

2
// AbstractType& operator=(const AbstractType&)!! Why??
// Expected `Type1& operator=(const AbstractType&)` to be called!
t1 = t2; 

Here you are calling:

t1.operator=(t2);

Since t1 and t2 have Type1, the compiler will match the following function:

Type1 & Type1::operator=(const Type1 &);

which is the implicitly-defined copy assignment operator, and which will call the copy assignment operator of the base:

AbstractType & AbstractType::operator=(const AbstractType &);

However, this call is not dynamically dispatched -- which is the reason you end up seeing your results.


Uncomment Type1& operator=(const Type1&) is just working for me

Note that:

  • There is no need for dynamic_cast.
  • There is no need to make a virtual call.
  • The operator itself does not need to be virtual.

In other words, you can simplify to:

Type1& operator=(const Type1& other)
{
    return Type1::operator=(static_cast<const AbstractType&>(other));
}
Acorn
  • 24,970
  • 5
  • 40
  • 69
  • Thank you for the answer! Then in the end, I have to two version of copy assignment anyway First one is `Type1& operator=(const AbstractType&)` Second one is `Type1& operator=(const Type1&)` Am I right? – 0ctopus13prime Jun 22 '18 at 02:14
  • You should probably only have standard assignment operator and change your design. However, your question does not have enough details to suggest you the right approach in your specific case. **In general case, virtual assignment is not a solution**. – Phil1970 Jun 23 '18 at 13:57
  • @0ctopus13prime: As Phil1970 says, it is hard to say how you should proceed. It is not clear, for instance, what are you trying to achieve with the "*more than a hundred of TypeX*" you refer to in the question. Are you trying to support assigning from any of those to any other? Or just to another of the same type? Why do you want to have virtual assignment in any case?, etc. – Acorn Jun 23 '18 at 15:37
1

Because of Liskovs substitution principle, which states that if a program, module or function is using a Base class, then the reference of Base class can be replaced with Derived class without affecting programs functionality. So in your particular case, implementing it in terms of Curiously recurring template pattern will be an elegant solution. Please see link for more info!

Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • Thank you for the answer. I thought to adopt that pattern, but it seems to me it is hard to do things in polymorphic way. Because I only have AbstractType references during the runtime it's very hard to know specific type. Fix me if I'm wrong! Thank you – 0ctopus13prime Jun 22 '18 at 02:31