问题
Case where the problem occours
Please consider the following c++ code:
#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();
}
In this example, we have two classes A
and B
. B
inherits from class A
. Both classes implement a simple virtual method that returns the type name.
There is also a simple method, print
, that accepts a reference to an instance of A
and prints the type.
The class State
holds a reference to an instance of A
. The class also has a simple method that calls print
by two different means.
Where it gets odd
The sole method in state first calls print
directly. Since we supply an instance of B
int the main method, the output is It's B
, as expected.
For the second call, however, we bind the instance to the first parameter of print
using std::bind
. Then we call the resulting function without any arguments.
In this case, however, the output is It's A
. I would have expected the output It's B
, as before, since it is still the same instance.
If I declare the parameters as pointers instead of references, std::bind
works as expected. I also placed some logging into the constructors of both classes to verify that no instances are created accidentally.
Why does this happen? Does std::bind
discard some type information in this case? To my understanding, this must not happen since the method invocation should be managed by a vtable lookup via runtime.
回答1:
This is just object slicing. Pass the instance by reference:
auto func = std::bind(&print, std::ref(instance));
// ^^^^^^^^
To explain this a bit more: Like most C++ standard library types, the result type of a bind
expression owns all its bound state. This means you can take this value and pass it around freely and store it and come back to it later in a different context, and you can still call it with all its bound state ready for action.
Therefore, in your code, the bind object was constructed with a copy of instance
. But since instance
wasn't a complete object, you caused slicing to happen.
By contrast, my code copies a std::reference_wrapper<A>
into the bind object, and that's essentially a pointer. It doesn't own the instance object, so I need to keep it alive as long as the bind object may get called, but it means that the bound call is dispatched polymorphically to the complete object.
来源:https://stackoverflow.com/questions/34366167/does-stdbind-discard-type-information-of-parameters-in-c11