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.
奇怪的地方
state中唯一的方法首先直接调用print
.由于我们在 main 方法中提供了一个 B
的实例,因此输出是 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
的副本 构造的.但是由于 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
复制到绑定对象中,这本质上是一个指针.它不拥有实例对象,所以只要绑定对象可能被调用,我就需要让它保持活动状态,但这意味着绑定调用被多态地分派给完整的对象.
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.
相关文章