出现问题的情况
请考虑以下c ++代码:
#include#include #include // 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
在main方法中提供了int 实例,因此输出It's B
正如预期的那样.
但是,对于第二个调用,我们将实例绑定到print
using 的第一个参数std::bind
.然后我们调用结果函数而不带任何参数.
但是,在这种情况下,输出是It's A
.我会It's B
像以前一样期望输出,因为它仍然是同一个实例.
如果我将参数声明为指针而不是引用,std::bind
则按预期工作.我还在两个类的构造函数中放置了一些日志记录,以验证是否没有意外创建实例.
为什么会这样?std::bind
在这种情况下会丢弃某些类型的信息吗?根据我的理解,这不应该发生,因为方法调用应该通过运行时的vtable查找来管理.
这只是对象切片.通过引用传递实例:
auto func = std::bind(&print, std::ref(instance)); // ^^^^^^^^
为了解释这一点:与大多数C++标准库类型一样,bind
表达式的结果类型拥有其所有绑定状态.这意味着您可以获取此值并自由传递并存储它,稍后在不同的上下文中返回它,您仍然可以在其所有绑定状态准备好进行操作时调用它.
因此,在您的代码中,绑定对象是使用的副本构造的instance
.但由于instance
不是一个完整的对象,你导致切片发生.
相比之下,我的代码将a复制std::reference_wrapper
到绑定对象中,而这实际上是一个指针.它不拥有实例对象,因此只要可以调用绑定对象,我就需要保持活动状态,但这意味着绑定调用将以多态方式分派给整个对象.