C++一般如何将成员函数转换为独立函数(用作函数参数)?
C++ how to convert member functions to standalone functions (for use as function parameter) generically?
对于模板函数的函数参数,我发现自己经常将成员函数包装在 lambda 中,以创建相同的独立函数,第一个参数是对象。
一个(虚拟)示例:
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?
您可以为此使用 std::bind()
。
Bind 接受一个函数,不管你想用它附带多少参数,returns 一个很好的 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);
请记住,非静态成员函数将 class 实例作为它们的第一个参数(尽管大多数时候它是隐式完成的,这不是其中之一),这就是为什么我们使用占位符。
当您现在执行 f(a)
时,a
(class 实例)将替换该占位符。
进一步阅读:Bind, Placeholders
您可以使用 C++14 通用 lambda 来帮助解决这个问题。像这样定义您的通用 lambda:
auto bindMem = [](auto f, auto& ... memArgs) { return [f, &memArgs...](auto& a) { return (a.*f)(memArgs...); }; };
摘要:这会创建一个通用的 lambda,调用它会产生另一个 lambda。第一个 lambda 获取您要调用的成员函数,以及任何绑定参数(this
对象除外)。它产生第二个 lambda,然后它需要对象本身并使用先前绑定的参数将成员函数调用应用于它。
因此对于您的用例,您拥有整洁的外观:
auto sum1 = calculateSum(as, bindMem(&A::f1));
auto sum2 = calculateSum(as, bindMem(&A::f2,i));
关于此的伟大之处在于完全相同的 bindMem
lambda 将适用于任何 class 和任何成员函数,具有任意参数列表。因此,从您的意思来看,它确实是通用的。
经过一番研究,感谢@happydave 的建议,我将采用以下答案:
auto sum1 = calculateSum(as, std::mem_fn(&A::f1));
第二个和不能这样处理,应该保留为 lambda。但是,一般来说,客户端代码是参数提供者的情况似乎不太可能(在这种情况下,无论如何都需要将参数传递给模板函数(lambda 捕获是一种很好的方法))。在许多情况下,模板函数还提供传递函数的参数,std::mem_fn 就可以了。
对于模板函数的函数参数,我发现自己经常将成员函数包装在 lambda 中,以创建相同的独立函数,第一个参数是对象。
一个(虚拟)示例:
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?
您可以为此使用 std::bind()
。
Bind 接受一个函数,不管你想用它附带多少参数,returns 一个很好的 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);
请记住,非静态成员函数将 class 实例作为它们的第一个参数(尽管大多数时候它是隐式完成的,这不是其中之一),这就是为什么我们使用占位符。
当您现在执行 f(a)
时,a
(class 实例)将替换该占位符。
进一步阅读:Bind, Placeholders
您可以使用 C++14 通用 lambda 来帮助解决这个问题。像这样定义您的通用 lambda:
auto bindMem = [](auto f, auto& ... memArgs) { return [f, &memArgs...](auto& a) { return (a.*f)(memArgs...); }; };
摘要:这会创建一个通用的 lambda,调用它会产生另一个 lambda。第一个 lambda 获取您要调用的成员函数,以及任何绑定参数(this
对象除外)。它产生第二个 lambda,然后它需要对象本身并使用先前绑定的参数将成员函数调用应用于它。
因此对于您的用例,您拥有整洁的外观:
auto sum1 = calculateSum(as, bindMem(&A::f1));
auto sum2 = calculateSum(as, bindMem(&A::f2,i));
关于此的伟大之处在于完全相同的 bindMem
lambda 将适用于任何 class 和任何成员函数,具有任意参数列表。因此,从您的意思来看,它确实是通用的。
经过一番研究,感谢@happydave 的建议,我将采用以下答案:
auto sum1 = calculateSum(as, std::mem_fn(&A::f1));
第二个和不能这样处理,应该保留为 lambda。但是,一般来说,客户端代码是参数提供者的情况似乎不太可能(在这种情况下,无论如何都需要将参数传递给模板函数(lambda 捕获是一种很好的方法))。在许多情况下,模板函数还提供传递函数的参数,std::mem_fn 就可以了。