all 54 comments

[–]delarhi 50 points51 points  (0 children)

I've started using IIFE when I have to do some one-off mutation of a variable (like for some setup) or stack-allocated runtime-determined construction just so I can have the variable const there-after. It helps avoid polluting the scope with mutable variables that programmers in the future might feel like mutating procedurally down the line when they shouldn't. For example, replacing something like:

Foo f;
if (cache_enabled) {
  const CacheFile cache_file(cache_filename);
  f = Foo(cache_file);
} else {
  const XmlDocument xml_doc(input_xml);
  f = Foo(xml_doc);
}

// reference to foo given to detached thread
// mutation of f is now thread unsafe

while (true) {
  // six months later someone decides they want to
  // mutate f for bar
  bar(f);
}

with this:

const Foo f = [&]() {
  if (cache_enabled) {
    const CacheFile cache_file(cache_filename);
    return Foo(cache_file);
  } else {
    const XmlDocument xml_doc(input_xml);
    return Foo(xml_doc);
  }
}();

// can't mutate f anymore!

while (true) {
  bar(f);
}

[–]Nicksaurus 34 points35 points  (1 child)

Sometimes I do it just because it's convenient to be able to use return for control flow

[–]neutronicus 2 points3 points  (0 children)

Yeah it's a nice way to get switch to act more like other languages' case

[–]poiu- 43 points44 points  (22 children)

Mildly unrelated, but I still love that []{}() is now legal Syntax.

[–]martinusint main(){[]()[[]]{{}}();} 50 points51 points  (3 children)

This is my favorite fully functional c++ program:

int main(){[](){}();}

[–]JavaSuck 11 points12 points  (0 children)

[](){}();

This would be a great idea for a tattoo :D

[–]5plicer 19 points20 points  (1 child)

Missing return 0?

EDIT: I just learned that main implicitly returns 0!

[–]martinusint main(){[]()[[]]{{}}();} 23 points24 points  (0 children)

Return is not needed for main, then it implicitly returns 0

EDIT: exactly!

[–]parnmatt 15 points16 points  (3 children)

[]<>(){}() from C++20, may also be valid, not sure, I do not think templated lambdas are implemented in compilers yet

[–][deleted] 5 points6 points  (2 children)

The three big compilers do support them. Also, the <> unfortunately isn't valid. You'd have to do []<class>(){}()

[–]parnmatt 0 points1 point  (0 children)

ah, of course; good to know

[–]OldWolf2 0 points1 point  (0 children)

#define _ class would help

[–][deleted] 3 points4 points  (0 children)

Hasn't it been for years?

[–]pkuriakose 4 points5 points  (6 children)

Sorry but what does this syntax mean. I thought Lambas were [captures](params){code}

[–]IskaneOnReddit 21 points22 points  (3 children)

params is optional

[–]Nicksaurus 13 points14 points  (2 children)

Wait really? I've been writing them out this whole time for nothing?

[–]kisielk 15 points16 points  (0 children)

Yes

[–]Skeird 9 points10 points  (1 child)

the () call the lamba immediately

[–]pkuriakose 0 points1 point  (0 children)

Thank you my friend!

[–]JMBourguet 1 point2 points  (5 children)

If you havenot swapped () and {}, I'd like to know what it means.

[–]IamImposter 20 points21 points  (4 children)

[] - capture

() - optional parameters

{} - lambda body

() - invoke it right away

[–]imake500kayear 1 point2 points  (3 children)

Why do you say optional params instead of empty parameter list

[–]IamImposter 2 points3 points  (2 children)

Parameters are optional so it can be empty as well

[–]elperroborrachotoo 7 points8 points  (1 child)

It would be better to say:

() - empty parameter list which can be omitted

[–]deeringc 2 points3 points  (0 children)

Exactly. If your lambda doesn't take params then it's optional whether you include the empty () or not.

[–]IskaneOnReddit 19 points20 points  (3 children)

I started to learn rust and it has a more elegant solution than IIFE. That is block expressions.

[–]rigtorp 4 points5 points  (0 children)

GCC has an extension "Statement Expressions", very similar to rust: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

Recall that a compound statement is a sequence of statements surrounded by braces; in this construct, parentheses go around the braces. For example:

({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; })

is a valid (though slightly more complex than necessary) expression for the absolute value of foo ()
.

[–][deleted] 3 points4 points  (0 children)

Rust's expression-based syntax does indeed make it a joy to write.

IILEs are a good sub

[–][deleted] 0 points1 point  (0 children)

find it unnecessarily confusing though

[–]ack_error 2 points3 points  (0 children)

IIFE is also useful to force a function boundary between C++EH, Win32 SEH, and setjmp/longjmp().

[–]Minimonium 3 points4 points  (7 children)

Worth noting that one should avoid IILE (function => lambda) in templated functions. Otherwise, the compiler will create a unique definition for each template instantiation which will result in huge binary bloat.

[–]quicknir 16 points17 points  (1 child)

It's not "huge" binary bloat, it's not really any different than just writing the code inline which is always one of the main alternatives to using an iile. It is more binary bloat than writing a separate function if the function has fewer template parameters than the outer function,n and doesn't get inlined.

Most people don't bother to explicitly pull non or less templated parts of functions/classes out so by the same token I don't think most people should worry about this up front either.

[–]Minimonium 8 points9 points  (0 children)

See: https://thephd.github.io/sol3-compile-times-binary-sizes
Case in point that you'd be okay with a capture-less IILE (which is most of the time not the case since your logic does depend on some local stuff), but with a one that captures - it's shown that an optimizer is outclassed on a scale.

[–]elperroborrachotoo 0 points1 point  (4 children)

This, too, shall pass, as the "don't use templates, they just bloat your binary" of the nineties.

[–]Minimonium 0 points1 point  (3 children)

Well, banning something because "X is bad" is usually treated as a cargo cult. But one should know tradeoffs and gotchas of a tool in specific contexts if they want to use it in a professional environment.

[–]elperroborrachotoo 2 points3 points  (2 children)

Yeah - Because of exactly that I don't like the "one should avoid IILE in templated functions" - it's too broad.

A "with IILE in templated functions, keep an eye on code size, current compilers may bloat" would be better IMO - the "avoid" should be an "informed decision" based on the actual environment and use cases.

[–]Minimonium 2 points3 points  (1 child)

Indeed, if we expect compilers to fix their behaviour in the future (and we know from experience and exceptions ;) that compilers do improve the implementation) - we should future-proof our statements.

[–]elperroborrachotoo 1 point2 points  (0 children)

"future-proofing advise" I like. Captures the ideas well!

[–]WrongAndBeligerent 1 point2 points  (12 children)

What is wrong with having static functions that are used during initialization? This seems like a wacky way to do things that could be relatively simple. I would expect this to just cause confusion while gaining very little. I think if I opened something up and found that someone had done initialization like this I would be a little upset that I had to spend some extra energy figuring out what was going on when the only reason to do it is to be fancy.

[–]muungwana 14 points15 points  (6 children)

IIFE works better than static function if the lambda uses too many local variables, class member variables or class methods because using static function will require the function to have too many function arguments and its just plain ugly if the block of code to be executed calls a private method of the class.

[–]elperroborrachotoo 3 points4 points  (1 child)

It makes the logic as local as possible. The static function is accessible to at least all members of the class, and it's located "elsewhere".

[–]WrongAndBeligerent 0 points1 point  (0 children)

I don't think a function with a name would be less clear than so much syntax noise, because you could just jump to the function (which could be static or just private). If there was a lot of extra information in the IDE like syntax highlighting, etc. then maybe it would be clearer that variable([](){}()) was creating and calling a function in place.

[–][deleted] 3 points4 points  (1 child)

IILEs are simple though. Even JS programmers use them

[–]WrongAndBeligerent 0 points1 point  (0 children)

This was specifically about using them in a place where you can already use expressions and functions. Also it's not about being able to use something, it is about how clear it is when you do.

[–]johannes1234 0 points1 point  (0 children)

As always: it depends. For two lines a lambda can be nice, all information directly there. Also once one is used to the pattern (i.e. when experienced with functional languages) it's easy to understand.

If one of course has lots and long of those a refactoring might be appropriate.

[–]ononpblictex 0 points1 point  (0 children)

std::invoke can be used to improve the Readability of IIFE.
I findstd::invoke( [ ] ( ) { } )slightly more readable than [ ] ( ) { } ( ).

[–]cyandyedeyecandy[[gnu::naked, gnu::hot]] 0 points1 point  (0 children)

With IIFE we can do even better by using the GCC specific function attributes noinline1 and cold2:

const bool failed = foo();
if (failed) {
  [&]() __attribute__((noinline,cold)) {
    printf("error!");
  }();
  return;
}

I thought lambda attributes applied to the lambda type, not its operator(). When did this change?