如何在C ++ 11中传递和执行匿名函数作为参数?

时间:2013-04-19 18:29:48

标签: c++ visual-c++ c++11 c++-cli

我正在寻找的代码如下。

bool Func1(int Arg1, C++11LambdaFunc Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }
}

稍后我将使用此代码。

Func1(12, [](int D) -> bool { ... } );

3 个答案:

答案 0 :(得分:44)

基本版本,用于头文件:

template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
  if(Arg1 > 0){
    return Arg2(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

更复杂的版本,如果您想从实现中拆分接口(它有运行时成本):

bool Func1(int Arg1, std::function<bool(int)> Arg2){
  if(Arg1 > 0){
    return Arg2(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

std::function使用类型擦除在lambda周围创建自定义创建的包装器,然后公开使用pImpl模式的非虚拟接口将其转发到自定义创建的包装器。 SUP> 1

或者,在技术性较差的情况下,std::function<bool(int)>是一个类,它可以包含几乎任何你可以调用的函数,传递一个与传递int兼容的参数,并返回一些东西与返回bool

兼容

通过std::function进行的调用的运行时间成本大致等于virtual函数调用(由上述类型擦除引起),当您创建它时,它必须复制状态传入的函数对象(aka functor)(可以是廉价的 - 无状态lambdas,或通过引用捕获参数的lambda - 或者在其他一些情况下很昂贵)并存储它(通常在免费商店或堆上,这有成本)虽然纯模板版本可以在调用点“内联”(即,不仅可以比函数调用成本更低,编译器甚至可以优化函数调用并返回边界!)

第一个示例的精美版本,它也更好地处理了一些极端情况:(也必须在头文件中实现,或者在使用的翻译单元中实现)

template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
  if(Arg1 > 0){
    return std::forward<Lambda>(Arg2)(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

使用称为“完美转发”的技术。对于某些仿函数,这会产生与#1略有不同的行为(通常更正确的行为)。

大部分改进来自于参数列表中&&的使用:这意味着传入对仿函数的引用(而不是副本),从而节省了一些成本,并允许const传递1}}或非 - const仿函数。

std::forward<Lambda>(...)更改只会导致行为更改,如果有人使用相对较新的C ++功能,允许方法(包括operator())覆盖{{1}的rvalue / lvalue状态指针。从理论上讲,这可能很有用,但我看到的基于this的右值状态实际覆盖的仿函数是this。当我写严肃的图书馆代码(tm)时,我会去打扰这个,但很少这样做。

还有一件事可以考虑。假设你想要一个返回0的函数,或者一个返回bool的函数,如果函数返回void,你想要把它当作它返回{{1} }。例如,在迭代某个集合时,您正在调用正在调用的函数,并且您希望选择性地支持早期暂停。该函数在想要提前停止时返回void,否则返回truefalse

或者,在更一般的情况下,如果你有一个函数的多个覆盖,其中一个取一个函数而另一个取一些其他类型在同一个位置。

这是可能的,这是我将要进入这里(使用智能适配器,或通过SFINAE技术)。但是,你可能最好只创建两个不同的命名函数,因为所需的技术太重了。


1 从技术上讲,true可以使用神奇的尘埃来做它所做的事情,因为它的行为是由标准描述的,而不是它的实现。我正在描述一个简单的实现,它近似于我与之交互的void实现的行为。

答案 1 :(得分:18)

第一个解决方案:

您可以将Func1()功能设为模板

template<typename T>
bool Func1(int Arg1, T&& Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }

    return false; // <== DO NOT FORGET A return STATEMENT IN A VALUE-RETURNING
                  //     FUNCTION, OR YOU WILL GET UNDEFINED BEHAVIOR IF FLOWING
                  //     OFF THE END OF THE FUNCTION WITHOUT RETURNING ANYTHING
}

然后您可以根据需要调用它:

int main()
{
    Func1(12, [](int D) -> bool { return D < 0; } );
}

第二个解决方案:

如果您不想使用模板,另一种选择(会带来一些运行时开销)是使用std::function

#include <functional>

bool Func1(int Arg1, std::function<bool(int)> Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }

    return false;
}

再次,这将允许您按照您希望的方式拨打Func1()

int main()
{
    Func1(12, [](int D) -> bool { return D < 0; } );
}

答案 2 :(得分:11)

对于那些口味更传统的人,请注意非捕获lambdas可以转换为函数指针。所以你可以把上面的函数编写为:

bool Func1(int Arg1, bool (*Arg2)(int)) { ... }

它将适用于传统函数 lambdas。