我试图更好地了解lambda如何捕获函子以及它与完美转发的关系。
我包含的代码只是一个最小的示例。我的真实代码使用可变参数模板,但这捕获了我一直遇到的大多数错误/困惑。我在不同的编译器上得到了不同的结果(Visual Studio 2017与gcc 4.9.3和7.2.0)。
“麻烦的”行似乎是has_functor在给定函数指针的情况下的实例,即在主函数中设置y。
我通过修改初始化toil_and_trouble的lambda参数,尝试了3种不同版本的代码。
版本1:通过引用捕获并转发...
[&](double d)->double{
return calls(std::forward<F>(f),d);
}
这对我测试过的所有编译器都有效,并产生正确的结果。但这使我感到紧张,因为当将右值传递到构造函数中时,可能会出现如下所述的UB:return a lambda capturing function parameter reference
版本2:这似乎是一种折衷,因为我们先按值接受,然后适当转发:
[=](double d) mutable ->double{
return calls(std::forward<F>(f),d);
}
这在VS上可以正常使用,但在gcc上却不能。我还认为,通过转发f(可能作为x值),lambda可能会丢失其捕获的函子,即其值可能会通过转发并用于其他函子而改变。
但是我看到的编译器错误实际上仅在推导F为double(&)(double,double)时发生,而在其他两种情况下则不会。
版本3:我认为转发可能是问题所在,所以我没有转发:
[=](double d) mutable ->double{
return calls(f,d);
}
这再次适用于VS,但不适用于gcc。
有人可以就UB可能出现的地方以及如何使用函数引用和右值正确管理实例化方面对这3个不同版本进行评论吗?
从C ++标准引用也很有帮助。
代码如下:
#include<utility>
#include<functional>
#include<iostream>
// functors
double foo(double, double) { return 0.; }
struct G{
double operator()(double,double) const{ return 0.; }
};
// helper
template<typename Fun>
double calls(Fun&& f, double d){
return f(0,d);
}
class has_functor{
private:
std::function<double(double)> toil_and_trouble;
public:
template<typename F>
has_functor(F&& f) : /* have tried multiple versions of this */
toil_and_trouble( [&](double d)->double{
return calls(std::forward<F>(f),d);
} ) {}
};
int main(){
auto x = has_functor(G{});
auto y = has_functor(foo);
auto z = has_functor([](double,double)->double{return 0.;});
}