Почему виртуальное назначение ведет себя иначе, чем другие виртуальные функции с одинаковой сигнатурой?
Играя с реализацией оператора виртуального присваивания, я закончил с забавным поведением. Это не сбой компилятора, поскольку g ++ 4.1, 4.3 и VS 2005 ведут себя одинаково.
По сути, виртуальный оператор = ведет себя иначе, чем любая другая виртуальная функция, в отношении кода, который фактически выполняется.
struct Base {
virtual Base& f( Base const & ) {
std::cout << "Base::f(Base const &)" << std::endl;
return *this;
}
virtual Base& operator=( Base const & ) {
std::cout << "Base::operator=(Base const &)" << std::endl;
return *this;
}
};
struct Derived : public Base {
virtual Base& f( Base const & ) {
std::cout << "Derived::f(Base const &)" << std::endl;
return *this;
}
virtual Base& operator=( Base const & ) {
std::cout << "Derived::operator=( Base const & )" << std::endl;
return *this;
}
};
int main() {
Derived a, b;
a.f( b ); // [0] outputs: Derived::f(Base const &) (expected result)
a = b; // [1] outputs: Base::operator=(Base const &)
Base & ba = a;
Base & bb = b;
ba = bb; // [2] outputs: Derived::operator=(Base const &)
Derived & da = a;
Derived & db = b;
da = db; // [3] outputs: Base::operator=(Base const &)
ba = da; // [4] outputs: Derived::operator=(Base const &)
da = ba; // [5] outputs: Derived::operator=(Base const &)
}
Эффект заключается в том, что виртуальный оператор = имеет поведение, отличное от любой другой виртуальной функции с той же сигнатурой ([0] по сравнению с [1]), вызывая базовую версию оператора при вызове через реальные производные объекты ([1] ) или производные ссылки ([3]), когда она работает как обычная виртуальная функция при вызове через базовые ссылки ([2]), или когда lvalue или rvalue являются базовыми ссылками, а другая - производной ссылкой ([4], [5]).
Есть ли разумное объяснение этому странному поведению?