Upcasted Assignment Fails

I'm curious what exactly is going on with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>

class Base
{
public:
  Base()
  {
    value_ = 0;
  }

  Base(int value)
  {
    value_ = value;
  }

  Base(const Base& original_instance)
  {
    value_ = original_instance.value_;
  }

  Base& operator=(const Base& rhs)
  {
    value_ = rhs.value_;
  }

  void set_value(const int value)
  {
    value_ = value;
  }

  int value()
  {
    return value_;
  }
protected:
  int value_;
};

class Derived : public Base
{
public:
  Derived() : Base() { }

  Derived(int value) : Base(value) { }

  Derived(const Derived& original_instance) : Base(original_instance) { }

  Derived& operator=(const Derived& rhs)
  {
    Base::operator=(rhs);
  }
};

int main(int args, char** argv)
{
  Derived derived(159);

  Derived foo(78);
  foo = derived; //implicitly uses Base::operator=()
  // Prints "Derived class value: 159"
  std::cout << "Derived class value: " << foo.value() << std::endl;

  Base base(22);
  //((Base) derived).operator=(base); //NO-OP
  ((Base) derived).Base::operator=(base); //NO-OP
  // Prints "Derived class value: 159"
  std::cout << "Derived class value: " << derived.value() << std::endl;

  derived.Base::operator=(base);
  // Prints "Derived class value: 22"
  std::cout << "Derived class value: " << derived.value() << std::endl;

  return 0;
}


There are two interesting things happening here.

First, the last assignment in main() suggests that even though I don't define a copy constructor or assignment operator in the derived class for base class objects, I can still copy the state of a base class object into a derived class object. If I deliberately wanted to prevent this (i.e. the derived class has tighter restrictions on the accepted values of the member data), how could I prevent this?

Second, simply upcasting the derived class instance makes the operator call a NO-OP (second assignment). Why doesn't this act like the last assignment?
For starters, your assignment operators lack return statements even though they are declared (rightfully) to return something.

As for your main(),

foo = derived;
This makes a call to Derived::operator=, which, in your implementation, explicitly calls Base::operator=, which performs the assignment.

((Base) derived).Base::operator=(base);
This copy-constructs a temporary nameless object of type Base using a reference to derived as its argument, and calls Base::oeprator= to assign from base to it. The temporary nameless Base, which now holds a copy of the value 22, is then destructed at the semicolon with no lasting effects.

derived.Base::operator=(base);
This calls Base::operator= on the Derived instance called "derived" using a Base called base as its argument. It performs the assignment as programmed.

What exactly do you want to prevent?

Note that, in general, a derived class cannot have "tighter restrictions" than the base class: a circle is not an ellipse http://en.wikipedia.org/wiki/Circle-ellipse_problem
Last edited on
Right, the return statements are missing. Just a sloppy example. Thanks for the explanations!

This came up while creating a templated Matrix class with derived classes SymmetricMatrix and HermitianMatrix. The derived classes have restrictions on the arrangements of their elements and in what operations can return an object of the same type. I do not want to be able to assign the elements of a general Matrix to, say, a SymmetricMatrix using the assignment operator from Matrix since this has no checks as to whether the arrangement of it's elements are symmetric under transposition. I'll probably have to do a protected inherit and redefine all public functions to call the base class functions.
This is a geniune design problem for matrix libraries. boost.ublas allows assignment from matrix to symmetric_matrix to compile, but fails it at run time if the contents of matrix are discovered to not be symmetric during copying (it throws an exception of type boost::numeric::ublas::external_logic: http://ideone.com/BiA3S )

I would check a few other high-profile C++ matrix libraries (Eigen etc) for inspiration.
Last edited on
Topic archived. No new replies allowed.