【问题标题】:C++ how to convert member functions to standalone functions (for use as function parameter) generically?C++ 一般如何将成员函数转换为独立函数(用作函数参数)?
【发布时间】:2017-03-30 19:56:05
【问题描述】:

对于模板函数的函数参数,我发现自己经常将成员函数包装在 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::function 与成员函数的工作方式与您在问题中解释的方式相同(但不是您的代码与f2 一起工作的方式)。不确定这是否更通用,因为您仍然需要在专业化中指定函数类型。
  • 你可能还想看看std::mem_fn
  • @happydave aaaaaa 我刚刚在撰写答案时重新发明了std::mem_fn。太好了。

标签: c++ c++11 lambda word-wrap member-functions


【解决方案1】:

您可以使用 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 将适用于任何类和任何成员函数,具有任意参数列表。因此,就您的意思而言,它确实是通用的。

【讨论】:

  • 此代码触发 UB,因为 memArgs 在您调用通过引用捕获它们的返回 lambda 时已经死亡。
  • 好的,我已经改为按值捕获它们
  • 实际上更好:我已经改回内部 lambda 中的引用捕获,但这次外部 lambda 通过 ref 阻止 UB 来获取它们
【解决方案2】:

经过一番研究,感谢@happydave的建议,我将采用以下答案:

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

第二个总和不能这样处理,应该保持为 lambda。但是,一般来说,客户端代码是参数提供者的可能性似乎较小(在这种情况下,无论如何都需要将参数传递给模板函数(并且 lambda 捕获是一种很好的方法))。在许多情况下,模板函数也提供传递函数的参数,std::mem_fn 就可以了。

【讨论】:

    【解决方案3】:

    您可以为此使用std::bind()

    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

    【讨论】:

    猜你喜欢
    • 2010-11-30
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 2013-06-29
    相关资源
    最近更新 更多