Friday, August 21, 2009

Reinitializing references in C++?

C++ references are aliases for the value they are initialized with. Once initialized, there is no way to "reseat" the reference to alias a different object or primitive. Right?

Almost. There is a special case that I thought of that allows a reference to be reseated.

Warning: the following is not recommended practice, just theoretical discussion. Don't do this unless you know exactly what you are doing. Really.


The Problem
First, suppose you have a class with a member reference:

class some_object{
public:
  some_object( int& some_int )
  : some_reference( some_int )
  { }
  int& some_reference;
  //...
};

Then you can write code as follows:

int a = 10;
int b = 15;
some_object obj1( a );
some_object obj2( b );
some_object obj3( obj2 );

But attempting to do assignment will result in an error:
obj2 = obj1;
GCC gives:
error: non-static reference member `int&some_object::some_reference', can't use default assignment operator
Attempting to supply an assignment operator won't help:

some_object& operator=( const some_object& other ) {
  some_reference = other.some_reference;
  return *this;
}

Since assignment operators do not have initialization lists, there is no way to "reinitialize" the reference. Therefore, this function will not "reseat" the reference, instead assigning the value of the integer to the other.

An odd C++ idiom
There is an ugly way to almost automatically implement the assignment operator for any class. I figured this out myself in an "Advanced OOP in C++" course, in a Eureka moment. It is also critiqued  by Guru of the Week at  http://www.gotw.ca/gotw/023.htm. The basic premise is that a quick and (very) dirty way to implement the assignment operator is as follows ( include <new>):

some_object& operator=( const some_object& other ) {
  if ( this != &other ) {
    this->~some_object();
    new ( this ) some_object( other );
  }
  return *this;
}
Going through this line-by-line
  • Make sure that the object is not being assigned to itself
  • Call the destructor on this object
  • Reconstruct the object, in-place using the copy-constructor ( the default copy-constructor in this case, but any copy constructor can work ), copying the other object
  • Return a reference to this object
Placement new, as being used here, constructs an object at a specified point in memory. Since the object called its own destructor, that memory is now free, and thus we are free to call placement new, to reconstruct the object on its old memory location. The fascinating thing that occurs, is that the member reference is copied as well ( "reseated" )!

Re-seating or Recreating
Is this really re-seating? After researching this online I found a few discussions around about this "technique". Some argue that this is indeed re-seating, while others say this is recreating the object in-place. Well its obviously both! By recreating the object in-place, an observer outside the assignment operator will assume the reference was re-seated. The method in which the reference was re-seated, is as we know, by destroying and recreating the object.

Other discussions on the topic:

Practical use?
I was unable to think of any practical use for this, but it is food for thought. Also, to comment on the GotW article, this idiom might have another safe use in certain cases, when using the Curiously Recurring Template Pattern, where the derived type is known in the base type, and slicing will not occur.