Boost:: Перегрузка и перегрузка виртуальных функций: почему они работают?

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

#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::...

Ответ 1

Указатель на метод 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.

Ответ 2

В основном 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 невозможно знать, что такое объект на самом деле, или что такое инварианты этого типа, поэтому то, что вы, вероятно, хотите сделать, это позволить динамической диспетчеризации делать свою магию.

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