Boost :: Bind и виртуальные функции перегружаются: почему они работают?

Я написал некоторый код и испугался, что он не будет работать — поэтому я написал прототип:

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>

class base {
private:
boost::function<void (int)> action;
protected:
virtual void onDataBaseReady(int i) { std::cout << i << std::endl; }
public:
void call() {
action(10);
}

base() {
action = boost::bind(&base::onDataBaseReady, this, _1);
}
};

class child : public base {
protected:
virtual void onDataBaseReady(int i) { std::cout << i+10 << std::endl; }
};

int main()
{
static child c;
c.call();
std::cin.get();
return 0;
}

это компилирует и работает. (выходы 20). Но почему? Также я проверил под VS2010 и интересно, будет ли он работать на разных платформах (скажем, скомпилирован под GCC)?

В основном action = boost::bind(&base::onDataBaseReady, this, _1); пугает меня — мы говорим &base::

11

Решение

Указатель на virtual метод делает virtual функция поиска при вызове.

#include <iostream>
#include <memory>

struct base {
virtual void foo() { std::cout << "base\n"; }
virtual ~base() {}
};

struct derived:base {
void foo() override final { std::cout << "derived\n"; }
};

int main() {
void (base::*mem_ptr)() = &base::foo;
std::unique_ptr<base> d( new derived() );
base* b = d.get();
(b->*mem_ptr)();
}

так что это «просто работает». Указатель на функцию-член (this->*&base::foo)() это не то же самое, что полный вызов функции this->base::foo(), Первый способ хранить foo часть вызова this->foo()Второй способ пропустить virtual поиск метода и прямой вызов base::foo,

15

Другие решения

В основном action = boost :: bind (&base :: onDataBaseReady, this, _1); пугает меня — мы говорим &база::…

На самом деле было бы гораздо страшнее, если бы он выполнял статическую диспетчеризацию, а не динамическую диспетчеризацию. Рассмотрим этот простой пример:

struct base {
virtual void foo() { /* maintain some invariants */ }
};
struct derived : base {
virtual void foo() { /* maintain different invariants */ }
};

А затем подумайте, что вы связываете функцию с родителем и вызываете ее для производного объекта. Разработчик derived знает, какие инварианты применяются к производному типу, который может быть тем же, подмножеством или полностью отличным от инвариантов в базовом типе.

void apply(base & b) {
std::bind(&base::foo, &b)();
}

Если диспетчеризация была разрешена во время привязки, и функтор был применен к производному типу (точного типа которого вы, возможно, не знаете!), То инварианты производного типа могут быть нарушены. В контексте apply Функция невозможна, чтобы узнать, что на самом деле является объектом, или каковы инварианты этого типа, так что вы, вероятно, хотите, чтобы динамическая диспетчеризация сделала свое волшебство.

[Это с точки зрения проектирования высокого уровня, даже не вдаваясь в детали, что вы не можете использовать указатель на элемент для выполнения статической диспетчеризации …]
1