std::bind绑定含mutex类成员函数时出现模板错误C2664咨询
问题触发根因
std::mutex属于不可拷贝、不可移动的类型,按照C++类默认函数的生成规则,只要类包含不可拷贝/不可移动的非静态成员,编译器就会隐式删除该类的拷贝构造函数和移动构造函数。你的类B因为直接持有std::mutex lock成员,天然不支持拷贝和移动操作。
你代码中std::bind(&B::foo, B())的逻辑,是要把传入的临时B实例存储到std::bind生成的可调用对象内部,这个存储过程要求被存储的类型要么支持拷贝、要么支持移动,才能把参数存到bind内部的tuple存储结构里。B两类操作都不支持,自然会触发构造失败的编译错误。
把mutex改成指针就能编译的原因也很简单:指针是原生标量类型,天然支持拷贝和移动,此时B不再包含不可拷贝/移动的成员,默认的拷贝/移动构造函数会被正常生成,满足bind的存储要求。
从tuple相关报错定位问题的方法
你看到的最上层报错:
error C2664: 'B::B(const B &)': cannot convert argument 1 from '_Ty' to 'const B &'
属于编译器在重载决议全部失败后抛出的表层错误,要定位根因需要顺着编译输出的模板实例化栈往下找:
- 首先你会在报错栈里看到
std::_Binder、std::tuple相关的模板实例化记录,最终指向你调用std::bind的代码行,说明错误发生在bind构造内部存储参数的tuple结构的过程中 - 继续往下看tuple的构造逻辑,会看到编译器尝试匹配
B的拷贝构造、移动构造重载时,因为B::lock(也就是std::mutex成员)的对应构造函数被标记为=delete,导致B的所有构造重载都无法匹配 - 最上层报错提示“无法转换为const B&”,本质是重载决议走到最后一步,尝试匹配const引用参数的拷贝构造也失败后抛出的通用错误,不是问题的本质原因。
可行修复方案
你可以根据场景选以下任意一种修复方式:
- 不需要bind持有B实例时,直接传对象指针或用
std::ref包装实例,避免拷贝B对象:
B b; // 传指针,bind内部只存指针值,不需要拷贝B auto callback3 = std::bind(&B::foo, &b); // 或者传引用包装,效果类似 // auto callback3 = std::bind(&B::foo, std::ref(b));
注意这种方式要保证b的生命周期比callback3更长,避免出现悬垂引用/指针。
- 确实需要bind持有B的独立实例时,把mutex成员改成智能指针类型,让B恢复可移动性:
class B { std::unique_ptr<std::mutex> lock = std::make_unique<std::mutex>(); std::string member = "Hello World!"; public: std::string foo() { std::lock_guard<std::mutex> g(*lock); return member; } };
- 优先用lambda替代
std::bind,语义更清晰,参数传递行为更可控,也更容易排查问题:
B b; auto callback3 = [&b]() { return b.foo(); };
内容的提问来源于stack exchange,提问作者dangur
相关产品推荐
相关产品推荐

