То, что вы делаете, не правильно, и в простом примере это будет работать, но может просто поднять ад (одна из возможностей неопределенного поведения) в других случаях.
Поскольку base::test
и derived::test
не являются виртуальными, это два разных метода-члена, поэтому для простоты я буду называть их base::foo
и derived::bar
. В коде связующего вы заставляете компилятор адаптировать указатель на bar
, который определен в derived
, как если бы он был фактически определен в base
, и затем вызываете его. То есть вы вызываете метод derived
для объекта или типа base
!!! что является неопределенным поведением.
Причина, по которой он не умирает, состоит в том, что указатели this
в base
и derived
совпадают и что вы обращаетесь только к данным, присутствующим в классе base
. Но это неверно.
Когда вы объявляете base::test
virtual, вы получаете правильное поведение: ваш самый производный объект в иерархии - base
, компилятор будет использовать механизм виртуальной диспетчеризации и обнаружит, что base
- это то место, где последний переопределитель для test
найдено и выполнено.
Когда вы объявляете только derived::test
как виртуальный (а не base
), компилятор попытается использовать несуществующий механизм виртуальной диспетчеризации (обычно указатель vtable) в передаваемом объекте, и это убивает приложение.
В любом случае, все, кроме виртуального base::test
использования, неверны. В зависимости от ваших реальных требований наиболее вероятный правильный способ это сделать:
class base {
public:
virtual bool test() const;
};
class derived : public base {
public:
virtual bool test() const; // <--- virtual is optional here, but informative
};
int main()
{
derived d; // <--- the actual final type
base & b = d; // <--- optional
if ( std::tr1::bind( &base::test, std::tr1::ref(b))() ) {
// ...
}
}
Обратите внимание, что нет приведения (приведения обычно являются намеком на что-то странное, там скрывается потенциально опасная вещь), что объект относится к конкретному типу, где вы хотите, чтобы метод вызывался, и что механизм виртуальной диспетчеризации гарантирует что даже если
привязка к base::test
, так как метод является виртуальным, будет выполнено окончательное переопределение.
Этот другой пример, скорее всего, сделает смешные вещи (я не пробовал):
struct base {
void foo() {}
};
struct derived : base {
void foo() {
for ( int i = 0; i < 1000; ++i ) {
std::cout << data[i];
}
}
int data[1000];
};
int main() {
base b;
std::tr1::bind((void (base::*)()) &derived::foo, std::tr1::ref(b))();
}