std :: bind会舍弃C ++ 11中参数的类型信息吗?
出现问题的地方
请考虑以下c ++代码:
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();
}
在此示例中,我们有两个类A
和B
. B
继承自类A
.这两个类都实现了一个简单的虚拟方法,该方法返回类型名称.
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.
还有一个简单的方法print
,它接受对A
实例的引用并打印类型.
There is also a simple method, print
, that accepts a reference to an instance of A
and prints the type.
类State
保存对A
实例的引用.该类还有一个简单的方法,可以通过两种不同的方式调用print
.
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.
奇怪的地方
处于状态的唯一方法首先直接调用print
.因为我们在main方法中提供了B
int的实例,所以输出如预期的那样是It's B
.
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.
但是,对于第二个调用,我们使用std::bind
将实例绑定到print
的第一个参数.然后,我们调用没有任何参数的结果函数.
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.
但是,在这种情况下,输出为It's A
.我应该像以前一样期望输出It's B
,因为它仍然是相同的实例.
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.
如果我将参数声明为指针而不是引用,则std::bind
会按预期工作.我还将一些日志记录到两个类的构造函数中,以验证是否没有意外创建实例.
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.
为什么会这样?在这种情况下,std::bind
是否会丢弃某些类型信息?据我了解,这一定不会发生,因为方法调用应通过运行时的vtable查找进行管理.
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.
这只是对象切片.通过引用传递实例:
This is just object slicing. Pass the instance by reference:
auto func = std::bind(&print, std::ref(instance));
// ^^^^^^^^
进一步解释一下:与大多数C ++标准库类型一样,bind
表达式的结果类型拥有的所有绑定状态.这意味着您可以使用此值并自由传递它,然后将其存储并在不同的上下文中稍后再使用它,并且仍可以将其绑定状态准备就绪以进行操作.
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.
因此,在您的代码中,绑定对象是使用instance
并不是一个完整的对象,所以您导致了切片的发生.
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.
相比之下,我的代码将std::reference_wrapper<A>
复制到绑定对象中,这实际上是一个指针.它不拥有实例对象,因此只要绑定对象可以被调用,我就必须保持它处于活动状态,但这意味着绑定调用将以多态方式分派给整个对象.
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.