1

I've got a simple base class and two classes that inherit from it. The problem is that the move assignment operators in each of the child classes aren't being used. Here's basically what I have:

class Parent {
public:
    virtual ~Parent();
    virtual Parent& operator=(Parent &&parent);
};

class Child1 : public Parent {
public:
    ~Child1();
    Child1& operator=(Child1 &&child);
};

class Child2 : public Parent {
public:
    ~Child2();
    Child2& operator=(Child2 &&child);
};

The implementations are littered with logs, so I know what's being called and when. Then, I run this (pseudo)code:

Parent &p {};
if (x == 1)
    p = Child1 {};
else
    p = Child2 {};

And I get output that looks something like this:

: Child constructor
: Parent move operator
: Child destruct
: SIGSEGV

Any idea what I'm doing wrong?

cetcet
  • 2,147
  • 2
  • 15
  • 15

3 Answers3

3

This is a classic case of slicing where you are assigning an instance of a derived type to an instance of a base type. To fix this you have to use dynamic allocation and pointers:

std::unique_ptr<Parent> p;

if(x == 1)
  p = std::make_unique<Child1>();
else
  p = std::make_unique<Child2>();
Chris Hayden
  • 1,104
  • 6
  • 6
  • Thanks - still learning about this. Unfortunately, std::make_unique isn't available for me. (I'm compiling with Xcode for cross-platform support: MacOSX, iOS, Android via JNI, etc.) Is there another way to do this? – cetcet Nov 10 '15 at 20:10
  • Unfortunately I'm not familiar with all of those platforms. If they don't have C++11 then they probably also don't have Boost, so raw new/delete might be the only option. That or you could roll your own smart pointer to handle memory management. – Chris Hayden Nov 10 '15 at 20:12
  • 1
    @cetcet yes `p.reset( new Child1 );` – Slava Nov 10 '15 at 20:24
  • 2
    @ChrisHayden `std::make_unique` is not available in c++11, only in c++14 – Slava Nov 10 '15 at 20:25
  • 1
    @Slava You're right! Hopefully @cetcet has C++11 and he can still use `unique_ptr`. – Chris Hayden Nov 10 '15 at 20:26
  • Yes, C++11. Also I'm stubborn and used to Objective C where the sort of shenanigan I'm trying to do here is commonplace. ;) – cetcet Nov 10 '15 at 21:27
2

C++ doesn't work that way. You can't just assign a subclass object to a base class object and expect that to work; this mistake is known as "object slicing".

Also, your if-statement's condition is not comparison, it's assignment.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
1

You have overloaded operator= for the following cases: (pseudocode)

Parent = Parent&&
Child1 = Child1&&
Child2 = Child2&&

However your code then attempts to do Parent = Child1, which wasn't one of those options. Note that the left-hand-side is the class type in which the function is defined - the return value type does not affect function overloading.

There is never implicit conversion from base class to derived class in C++, as that would be too dangerous. (You must use a cast to request that behaviour). For example, in this case it would be wrong to pass a Parent as argument to a function expecting a Child1 because that Parent is not a Child1.

However, there is implicit conversion from derived class to base class reference. So Parent = Child1 will match Parent = Parent&&. It cannot match any of the others because the left-hand-side is not implicitly converted to a derived class.

To solve this problem, your choices include:

  • Explicitly define Parent = Child1&& and Parent = Child2&& inside Parent (this will need forward declarations)
  • Have Parent = Parent&& use tag-dispatching or dynamic_cast or otherwise, to achieve the desired behaviour for all of the child classes

As mentioned by others, perhaps your design needs a re-think, as you are slicing on purpose here, but it is rare that slicing is an intended part of an object-oriented design. Possibly you intended Parent p; to actually be a reference or pointer to Parent, instead of an actual Parent.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Thanks for the reply. I don't want to convert anything. I suppose my mistake was not using references and/or pointers. – cetcet Nov 10 '15 at 20:13