如何立即调用 C++ lambda?

Posted

技术标签:

【中文标题】如何立即调用 C++ lambda?【英文标题】:How to immediately invoke a C++ lambda? 【发布时间】:2017-12-05 16:58:10 【问题描述】:

我正在继承的类的构造函数需要传入一个重要的对象。类似于此:

MyFoo::MyFoo() : SomeBase( complexstuff )

    return;

complexstuffMyFoo 关系不大,所以我不想传入。

我没有编写某种返回 complexstuff 的 1-off 临时函数,而是使用了 lambda。我花了几分钟才弄清楚我必须调用 lambda。所以我的代码现在看起来像这样:

MyFoo::MyFoo() : SomeBase(
    []()
    
        /* blah blah do stuff with complexstuff */
        return complexstuff;
     () )

    return;

如果你没有抓住它,那就是微妙的。但是在 lambda 主体之后,我不得不输入 () 来告诉编译器立即“运行” lambda。在我弄清楚我做错了什么之后,这是有道理的。否则,如果没有 () 来调用 lambda,gcc 会这样说:

error: no matching function for call to 'SomeBase(<lambda()>)'

但现在我开始思考——我这样做是否正确?在 C++11 或 C++14 中是否有更好的方法来告诉编译器我希望它立即调用我编写的 lambda?还是像我通常的做法一样附加一个空的()

【问题讨论】:

为什么要写一个 lambda 并立即调用它?这没有多大意义。 @n.m.:因为复杂的东西需要计算一些语句(比如一些for循环) @n.m.:为什么不呢?这基本上是实现 GCC 的 表达式语句 扩展提供的标准方法 (( ... ))。 IE。当您需要一个不仅仅涉及运算符的表达式时。 所以这是为了在表达式中嵌入语句。我理解正确吗? 就编码风格而言,// complexstuff // 应位于同一类 (MyFoo) 的私有静态成员函数中。换句话说,“1-off 临时函数” 是正确的方法。如果您的代码被其他人审查,您将被“纠正”。在这种情况下没有理由使用 lambda。我留下这条评论是因为显然有人的回答(不是我的)因为没有指出这个明显的问题而被否决(不是我)。这个问题是合理的,但代码示例对于某些 *** 用户来说可能是多余的。 【参考方案1】:

但现在我开始思考——我这样做对吗?

是的,你做到了。

在 C++11 或 C++14 中是否有更好的方法告诉编译器我希望它立即调用我编写的 lambda?

我不知道。一个 lambda 也只是一个函数对象,所以你需要有一个 () 来调用它,没有办法绕过它(当然除了一些像 std::invoke 这样调用 lambda 的函数) .

如果您愿意,可以在捕获列表之后删除(),因为您的 lambda 不带任何参数。

或者像我通常的方式那样附加一个空的()

是的,这是最短的方法。如前所述,std::invoke 也可以代替,但它需要更多的输入。我想说直接拨打() 是通常的方式。

【讨论】:

只是为了说明中间点,[]() 是一个空的 lambda 函数对象,[]() 是一个 void 表达式,由计算一个空的 lambda 得到。【参考方案2】:

在 C++17 中,您可以使用 std::invoke。这与您所做的完全相同,但也许您会发现这一点更清楚。

#include <iostream>
#include <functional>

void foo(int i)

  std::cout << i << '\n';


int main()

  foo( std::invoke( []()  return 1;  ) );

【讨论】:

这是 100% 没有意义的。 std::invoke 用于通用代码,您可能会在其中获得函数对象、PMF、函数指针等。当您已经知道 如何 调用您的可调用对象时,std::invoke 除了减慢速度之外没有其他用途构建时间。 @ildjarn 感谢您重复我在回答中所说的话并投反对票。 它不会“做完全相同的事情”,它做了很多更多,因此增加了构建时间。你没有提到那个。 ;-] 这是对std::invoke 的滥用,而不是对它的使用;即使它在某些情况下主观上“更清楚”,但无论您何时拥有固定形式的可调用对象,客观上都会更糟。 @ildjarn std::invoke 在这里用于强调函数调用,而不是依赖()-detail 可能会被阅读代码的人错过。仅此一项就可以节省开发人员的时间。该技术在本书中进行了解释:bfilipek.com/2016/11/…【参考方案3】:

没有办法告诉编译器立即调用 lambda。最简单的课程(就复杂性和键入字符的数量而言)是您已经完成的。对于使用过闭包语言的任何人来说,这也是非常惯用的(我在这里考虑的是 javascript)。

如果您想避免使用该语法,请修改 SomeBasecomplexstuff 以执行可调用对象。


如果你想要的只是调用 lambda 的语法糖,你总是可以做 Alexandrescu 的 SCOPE_GUARD 所做的事情,并滥用运算符重载:

Live example

#include <iostream>

namespace detail 

enum class invoke_t;

template<class Callable>
auto operator+(invoke_t, Callable c) -> decltype(c()) 
    return c();




constexpr detail::invoke_t invoke;


int main() 
    invoke + []() 
        std::cout << "called";
    ;

但我不会。发明你自己的 DSL 只会让你的代码更难维护。坚持使用简单语言结构的习语。

【讨论】:

operator+ 很有趣:P 从未见过这样的事情。你不能使用decltype(auto) 作为返回类型而不是尾随返回类型吗?他们不一样吗? 发明你自己的 DSL 会使你的代码在大多数情况下更难维护。但是对于一些罕见的情况,它可以使代码更易于维护。例如,fn("myFunctionName")(1, "hi", cast("void *")(variable("var"))) 之类的东西比Function "myFunctionName", FunctionArgument std::to_string(1) , FunctionArgument "hi" , FunctionArgument Cast "void *", Variable "var" 更易读(尤其是当它变得更复杂时)。它只要求维护者理解 dsl @Rakete1111 - OP 标记为 C++11,我只是坚持那个领域。当然,您可以使用返回类型推导。 @StoryTeller 好吧,在问题中 OP 也提到了 C++14,所以我只是在问题中添加了那个标签...... :) @Justin - 有很多 ifs :) 我同意 DSL 本质上并不是一件坏事(想到 SCOPE_GUARD、重载运算符| 用于范围等等),但它非常重要一个判断电话。【参考方案4】:

有没有更好的办法

您还可以考虑使用私有静态成员函数来构建 complexstuff,类似于

class MyFoo : public Base 
private:
    static SomeComplexType compute_complex_stuff() 
      SomeComplexType complexstuff;
      /*compute the complexstuff */
      return complexstuff;
    ;
public: 
    MyFoo() : Base(compute_complex_stuff()) ;
;

我不知道它是否比定义一个 lambda 表达式并立即应用它更好;恕我直言,这是一个口味问题;对于 short lambda 主体,我更喜欢立即应用 lambda 表达式(但也许某些编译器会在这种情况下创建临时闭包,因此如果没有优化它可能会更慢;我希望大多数 C++11 编译器能够进行优化)。

顺便说一句,GCC 为您的目的提供了 statement expression 语言扩展(Clang 也可以理解)。有了它,你可以写

MyFoo::MyFoo : Base ((
  SomeComplexType complexstuff;
  /*compute the complexstuff */
  return complexstuff;
) ;

【讨论】:

以上是关于如何立即调用 C++ lambda?的主要内容,如果未能解决你的问题,请参考以下文章

贯穿 C++ 11 与 C++ 17 的 Lambda 到底是个什么?

c++基础(lambda)

如何将 lambda 表达式作为参数传递给 c++ 模板

如何立即取消 curl 操作?

如何阻止 C++ 控制台应用程序立即退出?

如何使用 C++ lambda 将成员函数指针转换为普通函数指针以用作回调