Search code examples
c++11referencepolymorphismruntimevirtual

Why does the compiler allow references to be used for run-time polymorphism in C++?


My concern is about run-time polymorphism. I quite understand the notion of using base-class pointers and the "virtual" keyword in order to achieve it. And I do understand why the compiler defers call resolution to run-time. But, I am confused thinking why the compiler would defer it when references are used, instead of pointers. A reference, once assigned, cannot refer to anything else. So, doesn't the compiler already know what object the reference refers to? So, why doesn't it statically resolve function calls when there is a base-class reference, not pointer? This is different for a pointer because it can point to any object at run-time within the class hierarchy. But the reference is fixed!

Here is a code snippet:

class Base
{
protected:
   int m_value;

public:
   Base(int value)
    : m_value(value)
   {
  }

   virtual const char* getName() const { return "Base"; }
   int getValue() const { return m_value; }
};

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

virtual const char* getName() const { return "Derived"; }
};

int main()
{
   Derived derived(5);
   std::cout << "derived is a " << derived.getName() << " and has value " << 
   derived.getValue() << '\n';

Base &ref = derived;
std::cout << "ref is a " << ref.getName() << " and has value " << ref.getValue() << '\n';

Base *ptr = &derived;
std::cout << "ptr is a " << ptr->getName() << " and has value " << ptr- >getValue() << '\n';

return 0;
}

Solution

  • "The reference is fixed" premise is false. A reference may refer to the base subobject of any object in the hierarchy, just like the pointer can. The compiler cannot tell from the reference what the most-derived object is, no more than it can from the pointer.

    Consider:

    void DoSomething(const Base& b) { std::cout << b.getName(); }
    
    Base base;
    DoSomething(base);  // b is bound to Base object. Prints "Base"
    
    Derived derived;
    DoSomething(derived);  // b is bound to Base subobject of Derived object.
                           // Prints "Derived"