How to immediately invoke a C++ lambda?

13,939

Solution 1

But now that has me thinking -- did I do this correctly?

Yes you did.

Is there a better way in C++11 or C++14 to tell the compiler that I want it to immediately invoke a lambda I've written?

Not that I know of. A lambda is also just a function object, so you need to have a () to call it, there is no way around it (except of course some function that invokes the lambda like std::invoke).

If you want you can drop the () after the capture list, because your lambda doesn't take any parameters.

Or is appending an empty () like I did the usual way to do this?

Yes, it is the shortest way. As said before, std::invoke would also work instead, but it requires more typing. I would say a direct call with () is the usual way it is done.

Solution 2

In C++17 you can use std::invoke. This does the exact same thing as you did, but perhaps you will find this clearer.

#include <iostream>
#include <functional>

void foo(int i)
{
  std::cout << i << '\n';
}

int main()
{
  foo( std::invoke( []() { return 1; } ) );
}

Solution 3

There's no way to tell the compiler to invoke the lambda immediately. The simplest course (both in terms of complexity and number of typed characters) is what you did already. It's also very idiomatic for anyone who has worked with languages that have closures (I'm thinking JavaScript here).

If you want to avoid the syntax, then either modify SomeBase or complexstuff to execute the callable.


If all you want is syntactic sugar for invoking the lambda, you can always do what something like Alexandrescu's SCOPE_GUARD does, and abuse operator overloading:

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";
    };
}

But I wouldn't. Inventing your own DSL will just make your code worse to maintain. Stick to the idioms that utilize plain language constructs.

Solution 4

Is there a better way

You could also consider having a private static member function building the complexstuff, something like

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

I don't know if it is better than defining a lambda expression and applying it immediately; that is IMHO a matter of taste; for a short lambda body I would prefer a lambda expression immediately applied (but perhaps some compiler would create the temporary closure in that case, so it might be slower without optimizations; I expect most C++11 compilers to be able to make that optimization).

BTW, GCC provides the statement expression language extension (also understood by Clang) for your purposes. With it you could write

MyFoo::MyFoo : Base (({
  SomeComplexType complexstuff;
  /*compute the complexstuff */
  return complexstuff;
}) {};
Share:
13,939

Related videos on Youtube

Stéphane
Author by

Stéphane

Linux, Ubuntu, C++ developer. https://www.linkedin.com/in/scharette http://www.ccoderun.ca/

Updated on June 04, 2022

Comments

  • Stéphane
    Stéphane almost 2 years

    A constructor from a class I'm inheriting requires a non-trivial object to be passed in. Similar to this:

    MyFoo::MyFoo() : SomeBase( complexstuff )
    {
        return;
    }
    

    The complexstuff has little to do with MyFoo, so I didn't want to have to pass it in.

    Instead of writing some kind of 1-off temporary function that returns complexstuff I used a lambda. What took me a few minutes to figure out is I have to invoke the lambda. So my code now looks like this:

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

    If you didn't catch it, it is subtle. But after the lambda body, I had to put () to tell the compiler to immediately "run" the lambda. Which made sense after I figured out what I had done wrong. Otherwise, without the () to invoke the lambda, gcc says something similar to this:

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

    But now that has me thinking -- did I do this correctly? Is there a better way in C++11 or C++14 to tell the compiler that I want it to immediately invoke a lambda I've written? Or is appending an empty () like I did the usual way to do this?

    • Basile Starynkevitch
      Basile Starynkevitch almost 7 years
      @n.m.: because the complexstuff needs some statements to be computed (such as some for loop)
    • AnT stands with Russia
      AnT stands with Russia almost 7 years
      @n.m.: Why not? This is basically the standard way to achieve what GCC's expression statements extension is offering (({ ... })). I.e. when you need an expression that involves more than just operators.
    • n. m.
      n. m. almost 7 years
      So this is for embedding statements in an expression. Did I understand it correctly ?
    • rwong
      rwong about 6 years
      As a matter of coding style, the // complexstuff // should be in a private static member function of the same class (MyFoo). In other words, "1-off temporary function" would have been the correct approach. You would have been "corrected" if your code were reviewed by other people. There is no reason to use lambda in this case. I leave this comment because apparently someone's answer (not mine) got downvoted (not me) for not pointing out this obvious issue. The question is legitimate but the code sample might have seemed superfluous to some StackOverflow users.
  • Potatoswatter
    Potatoswatter almost 7 years
    Just to illustrate the middle point, [](){} is an empty lambda function object, and []{}() is a void expression resulting from evaluating an empty lambda.
  • Justin
    Justin almost 7 years
    Inventing your own DSL will make your code worse to maintain for most purposes. But for some rare cases, it can make the code easier to maintain. For example, something like fn("myFunctionName")(1, "hi", cast("void *")(variable("var"))) is a lot more readable than Function{ "myFunctionName", { FunctionArgument{ std::to_string(1) }, FunctionArgument{ "hi" }, FunctionArgument{ Cast{ "void *", Variable{ "var" } } } } } (especially if it gets more complex). It just requires that the maintainer understands the dsl though
  • StoryTeller - Unslander Monica
    StoryTeller - Unslander Monica almost 7 years
    @Justin - That's a lot of ifs :) I agree that a DSL isn't inherently a bad thing (SCOPE_GUARD, overloading operator| for ranges and so forth come to mind), but it's very much a judgement call.
  • ildjarn
    ildjarn almost 7 years
    This is 100% pointless. std::invoke is for generic code where you might be given a function object, PMF, function pointer, etc. When you already know how to invoke your callable, std::invoke serves no purpose other than to slow build times.
  • Henri Menke
    Henri Menke almost 7 years
    @ildjarn Thank you for repeating what I said in the answer and downvoting.
  • ildjarn
    ildjarn almost 7 years
    It doesn't "do the exact same thing", it does a lot more, hence the increased build times. You didn't mention that. ;-] This is an abuse of std::invoke more than a use of it; even if it's subjectively "clearer" in some context, it's objectively worse regardless when you have a fixed form of callable.
  • alfC
    alfC over 3 years
    @ildjarn std::invoke is used here to stress out the function invocation and not rely the ()-detail which can be missed by the person reading the code. This alone can save developer's time. The technique is explained in this book: bfilipek.com/2016/11/…
  • Dietmar Kühl
    Dietmar Kühl about 3 years
    I guess, invoke should be declared inline to allow definitions in multiple translation units: inline constexpr enum {} invoke{};
  • StoryTeller - Unslander Monica
    StoryTeller - Unslander Monica about 3 years
    @DietmarKühl - I was relying on the implied internal linkage of const namespace scoped objects. But now I see that probably made the template a big odr violation on account of the annonymous type of the object in every TU. It should be fixed now, and be valid C++11 (my original intent).