2016-11-16 62 views
3

对于模板函数的函数参数,我发现自己经常在lambda中包装成员函数以创建相同的独立函数,第一个参数是对象。C++如何将成员函数一般转换为独立函数(用作函数参数)?

A(虚设)例如:

class A 
{ 
public: 
    double f1() const; 
    double f2(int i) const; 

    // some data 
}; 

template <typename Func> 
double calculateSum(std::vector<A> as, Func f) 
{ 
    double result = 0.0; 
    for (auto a : as) 
     result += f(a); 
    return result; 
} 

int main() 
{ 
    std::vector<A> as; 
    int i = 0; 
    auto sum1 = calculateSum(as, [](const A& a) { return a.f1(); }); 
    auto sum2 = calculateSum(as, [&i](const A& a) { return a.f2(i); }); 
    return 0; 
} 

有一种方法,以更一般定义这样的lambda?或者有没有办法直接引用成员函数而不是使用lambdas?

+0

具有成员函数的'std :: function'以您在问题中解释的方式工作(但不以您的代码与'f2'工作的方式)。不知道这是否更通用,因为你仍然需要在专业化中指定函数类型。 –

+2

你可能也想看看'std :: mem_fn' – happydave

+1

@happydave aaaaaaand我刚刚在编写我的答案时重新创建了'std :: mem_fn'。大。 – Quentin

回答

2

一些经过研究,由于@happydave的建议,我会用下面的答案去:

auto sum1 = calculateSum(as, std::mem_fn(&A::f1)); 

第二总和不能处理这样,应该保持一个lambda。但是,通常情况下,客户端代码是参数供应商的可能性较小(在这种情况下,参数需要传递给模板函数(并且lambda捕获是一种很好的方式))。在很多情况下,模板函数也提供了传递函数的参数,std :: mem_fn也没问题。

2

您可以使用std::bind()

绑定需要一个函数,然而无论你想使用多少参数并返回一个不错的std::function对象。您可以在创建时指定参数,或者在调用返回的函数时使用占位符指定它们。

#include <functional> 
... 
auto sum1 = calculateSum(as, std::bind(&A::f1, std::placeholders::_1)); 
auto sum2 = calculateSum(as, std::bind(&A::f2, std::placeholders::_1, i); 

请注意,非静态成员函数采取一个类的实例作为他们的第一个参数(尽管大多数的它的隐式进行的时候,这是不是其中之一),这就是为什么我们使用的占位符。 现在,当您执行f(a)时,该a(类实例)将替代该占位符。

延伸阅读:BindPlaceholders

+4

假设C++ 14,我认为普遍的共识是[lambda应该始终优于std :: bind](http://stackoverflow.com/a/17545183/3282436)。 – 0x5453

+1

@ 0x5453谢谢。我其实并没有意识到这些缺点。 –

4

您可以使用C++ 14个lambda表达式通用,以帮助这一点。定义你的通用拉姆达这样的:

auto bindMem = [](auto f, auto& ... memArgs) { return [f, &memArgs...](auto& a) { return (a.*f)(memArgs...); }; }; 

消化:这将创建一个通用的λ,调用它的产生另一个lambda。第一个lambda获取您想要调用的成员函数,以及任何绑定的参数(除this对象外)。它产生了第二个lambda,它只是期待对象本身,并将其成员函数调用与先前绑定的参数一起使用。

所以对你的使用情况,您有整齐的前瞻性:

auto sum1 = calculateSum(as, bindMem(&A::f1)); 
auto sum2 = calculateSum(as, bindMem(&A::f2,i)); 

关于这个伟大的事情是完全一样的bindMem拉姆达将任何类和任何成员函数工作,任意参数列表。所以在你的意思上它确实是通用的。

+0

这段代码触发了UB,因为当您调用通过引用捕获它们的返回lambda时,'memArgs'已经死亡。 – Quentin

+0

好吧,我已经改变了捕获他们的值,而不是 – Smeeheey

+1

实际上更好的是:我已经改回到内部lambda中的引用捕获,但这次外层lambda通过ref阻止UB – Smeeheey

相关问题