为何传递指针与引用时std::bind表现存在差异?
关于std::bind绑定虚成员函数的行为疑问
需求为获取指向Base::Print的仿函数,以下是测试代码:
#include <functional> #include <iostream> using namespace std; class Base { public: virtual void Print() { cout << "Base" << endl; } virtual function<void()> get_fx1() { return bind(&Base::Print, this); } virtual function<void()> get_fx2() { return bind(&Base::Print, *this); } }; class Derived : public Base { public: virtual void Print() override { std::cout << "Derived" << std::endl; } }; int main() { Derived d; Base *ptr = &d; ptr->Base::Print(); // 输出Base(符合预期)#out1 bind(&Base::Print, ptr)(); // 输出Derived(预期为Base)#out2 ptr->get_fx1()(); // 输出Derived(预期为Base)#out3 ptr->get_fx2()(); // 输出Base(与out2/out3表现矛盾)#out4 cout << " -- " << endl; Derived d2; Base& ref = d2; ref.Base::Print(); // 输出Base(符合预期)#out5 bind(&Base::Print, ref)(); // 输出Base(与out2/out3表现矛盾)#out6 cout << " -- " << endl; (&ref)->Base::Print(); // 输出Base(符合预期)#out7 bind(&Base::Print, &ref)(); // 输出Derived(与out6表现矛盾)#out8 return 0; }
原本认为向std::bind传递指针或引用的效果完全相同,但实际表现差异明显:传递指针时绑定&Base::Print,调用后执行的是Derived的重写函数;传递引用时绑定,调用后执行的是Base的原函数。疑问是:std::bind是否会在传递引用时选择性跳过虚表查找?目前未找到相关官方说明。
原因解析
这不是std::bind选择性跳过虚表,而是成员函数指针的调用规则与std::bind的参数值拷贝行为共同作用的结果:
- 虚成员函数指针的本质
&Base::Print是一个虚成员函数指针,即便用类名限定,也不会改变函数的虚属性。调用时是否触发多态,完全取决于调用时的对象类型:
- 通过指针/引用调用:会触发虚表查找,执行对象实际类型的重写函数;
- 通过对象值调用:对象会被切片为Base类型,直接执行Base的成员函数。
- std::bind的参数处理逻辑
- 传递指针
ptr给bind时,bind会存储指针的副本。调用仿函数时,等价于(ptr->*&Base::Print)()——通过指针调用虚函数,触发多态,因此执行Derived的Print。 - 传递
*this或ref给bind时,bind默认会存储对象的值拷贝。*this在Base成员函数中是Base&,拷贝后成为Base对象;ref是Base&,拷贝后同样是Base对象。调用仿函数时等价于(obj.*&Base::Print)()——通过切片后的Base对象调用,不触发多态,因此执行Base的Print。
验证结论
如果修改get_fx2,用std::ref让bind存储对象引用而非拷贝:
virtual function<void()> get_fx2() { return bind(&Base::Print, std::ref(*this)); }
此时调用ptr->get_fx2()()会输出Derived,因为std::ref保留了引用关系,调用时通过引用触发多态。
内容的提问来源于stack exchange,提问作者Samarth S
相关产品推荐
相关产品推荐

