求助:std::bind与lambda导致限定符丢弃的原因及不解的解决方案
最近我碰到一个编译错误,始终搞不清限定符是在哪、为什么被丢弃的,先贴出相关代码:
#include <iostream> #include <memory> class A { public: void f() {}; }; template <typename Callable> void invoker(Callable c) { auto l = [=]() { c(); // <------------------- 错误行 }; l(); } int main() { A a; invoker(std::bind(&A::f, a)); return 0; }
编译时在c();这行会报出如下错误:
error: passing ‘const std::_Bind(A)>’ as ‘this’ argument of ‘_Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {}; _Result = void; _Functor = std::_Mem_fn; _Bound_args = {A}]’ discards qualifiers [-fpermissive]
错误根源
问题的核心在于:当lambda用[=]按值捕获c时,捕获到的是std::bind生成的绑定器对象的const副本。而我们要调用的A::f()是非const成员函数,它要求this指针是非const的,但const版本的绑定器在调用时,无法提供非const的this给成员函数,因此编译器报错说“丢弃了限定符”。
接下来逐个拆解三种可行的解决方案:
方案1:将A::f()定义为const成员函数
修改A类的成员函数:
void f() const {};
这样f()变成了const成员函数,它允许被const对象调用。lambda里的const绑定器副本调用它时,完全符合const成员函数的调用规则,自然就不会有冲突了。
方案2:用std::ref绑定实例a的引用
修改main里的调用代码:
invoker(std::bind(&A::f, std::ref(a)));
这里std::ref让std::bind绑定的是a的引用而非副本。此时绑定器对象内部保存的是原对象的引用,即便绑定器被lambda捕获为const副本,调用时实际操作的是原非const对象a,非const成员函数f()就能正常执行。
方案3:lambda按引用捕获c
修改invoker里的lambda:
auto l = [&]() { c(); };
把捕获方式从值捕获[=]改成引用捕获[&],这样lambda里的c是原绑定器对象的引用,而非const副本。调用它的operator()时不需要丢弃const限定符,就能顺利调用非const的A::f()。
内容的提问来源于stack exchange,提问作者hudac

