Случай возникновения проблемы
Пожалуйста, рассмотрите следующий код С++:
#include <functional>
#include <iostream>
#include <string>
// Superclass
class A {
public:
virtual std::string get() const {
return "A";
}
};
// Subclass
class B : public A {
public:
virtual std::string get() const {
return "B";
}
};
// Simple function that prints the object type
void print(const A &instance) {
std::cout << "It's " << instance.get() << std::endl;
}
// Class that holds a reference to an instance of A
class State {
A &instance;
public:
State(A &instance) : instance(instance) { }
void run() {
// Invokes print on the instance directly
print(instance);
// Creates a new function by binding the instance
// to the first parameter of the print function,
// then calls the function.
auto func = std::bind(&print, instance);
func();
}
};
int main() {
B instance;
State state(instance);
state.run();
}
В этом примере у нас есть два класса A
и B
. B
наследуется от класса A
. Оба класса реализуют простой виртуальный метод, который возвращает имя типа.
Существует также простой метод print
, который принимает ссылку на экземпляр A
и печатает тип.
Класс State
содержит ссылку на экземпляр A
. В классе также есть простой метод, который вызывает print
двумя разными способами.
Где это становится странным
Единственный метод в состоянии сначала вызывает print
напрямую. Поскольку мы передаем экземпляр B
в основной метод, вывод будет It's B
, как и ожидалось.
Однако для второго вызова мы привязываем экземпляр к первому параметру print
, используя std::bind
. Затем мы вызываем полученную функцию без каких-либо аргументов.
Однако в этом случае выход равен It's A
. Я ожидал вывода It's B
, как и раньше, так как это все тот же экземпляр.
Если я объявлю параметры как указатели, а не ссылки, std::bind
будет работать как положено. Я также поместил некоторые журналы в конструкторы обоих классов, чтобы убедиться, что экземпляры не создаются случайно.
Почему это происходит? Отбрасывает ли std::bind
некоторую информацию о типе в этом случае? Насколько я понимаю, этого не должно происходить, поскольку вызов метода должен управляться поиском в виртуальной таблице через среду выполнения.