With the struct at the beginning, you could skip the extra parenthesis by instantiating an anonymous struct: int main() { struct { int operator()() { return 42; } } get_value; return get_value(); }
@@reubenfrench6288 Which part is non-standard? It works with GCC, Clang and MSVC but anyway, that's just a fun experiment, not something you should write. Lambdas are the way to go
To be more readable, I usually make a variable with lambda, then give it to the algorithm: auto comp = [](const auto key){ return key < 42; }; auto it = std::find(std::begin(v), std::end(v), comp);
What a coincidence, I happened to have been reading about the GCC local functions extension a couple days ago thinking “woah, I don’t know anyone who has talked about this”
it remind me the streams of Bob Ross when there are comments like " 50 % he is going to draw a cabin" and the chat go wild but in Jason case its "50% he is going to write a lambda".
Lambdas are great and all but I do like local functions. C# introduced local functions a few years ago and I really like it (I personally find it looks cleaner).
auto func(Args&&... args) -> ReturnType { } auto func = [ ](Args&&... args) -> ReturnType { }; pretty much the same in practice, although doing different things. i’d say it’s not a stretch stating that local functions can exist through lambdas, that’s very intuitive...
GCC C compiler have extension Nested-Functions that allow this, interesting they have similar functionality to `[&]` lambda as can reference objects from parent function
@@platin2148 blocks is clang's extension and is much closer to actual lambda functions we have in C++. It has different syntax from normal functions (somewhat similar to function pointer syntax), the data is stored either statically or on the heap, and the function part is just treated like a normal function, so there are no security issues like with GCC's Nested Functions. Nested Functions in GCC have the same syntax as normal functions and basically store a closure in the current stack frame and make the stack executable (which is why they're dangerous)
@@embeddor2230 What is unsafe about it? All I've read is that you can take the address and call if from anywhere, accessing stack variables after they're out of scope. Which is dangerous but it's as expected as referencing any local variable after its scope.
there are some languages that do support local functions (e.g., some dialects of Pascal) but. i find C++ lambdas a superior alternative as can specify the exact manner of closure over outer scope variables of the host function body - which is ay safer than just some sort of defaulting to closure over every outer scope variable in some generic manner of semantics - C++ allows specifying either value or reference semantics once something resembling local functions exist it really needs to have offer up control over manner of outer scope closure or else is just another gun that will end up shooting a foot off
Except that in Pascal you can store function pointer to capturing "lambda" and in C++ you can't, which I think makes them inferior to Pascal ones. You can also have a non-capturing nested functions that will have access to all previously declared variables in the parent function. I find funny that you are commenting about shooting a foot off, because C++ is literally the number one language in that regard.
None of your non-lambda solutions attempted to use variables in their defining scope act as globals. Fortran statement functions definitely did this. I don't remember about Pascals nested functions. The lambda capture also solves this problem.
They can be defined, but only in the surrounding scope. For example: int main() { void foo(); foo(); } void foo() {} I still find little point to this, especially with the tradeoff that allowing this is what gives rise to MVP, but it isn't a dead end.
I don’t think there was ever a point to it, per se. Allowing it was just a side-effect of the grammar, and there was no reason to explicitly disallow it.
The lack of local functions bugged me a lot coming from pascal to different languages. Fortunately most languages fixed this with the introduction of lambdas
And if I understood right, these lambas's captures and their states are persistent, so even though we may exit the function scope, upon next entry to the function the lamba and reuse its captured captures states.
If you capture by reference, you must be careful to ensure that lifetimes of the referenced variables don't end. Calling a lambda with a dangling reference results in undefined behavior. If you capture by value, the captured values do stay with the lambda as long as it exists.
Haven't watched yet, but I just wanted to pause and start out by saying that I LOVE embedding unexpected things in function scopes. It makes people so angry!
Yeah... Allowing the call operator to be static seems like a bad idea that would break everything and only be useful for implementing bad ideas.... I mean I'm open to hearing examples, but to be honest that's just because I'm certain to get a laugh out of it.
That's what local functions do. They separate something out into a new function with its own name that a) doesn't clutter the surrounding scope if it's only used in one place; and b) doesn't require a bunch of boilerplate to pass in local state.
It can go both ways. Having a bunch of single-use functions that are in global scope can also make code harder to reason about. The hard part is finding the mix that works best.
With the struct at the beginning, you could skip the extra parenthesis by instantiating an anonymous struct:
int main() {
struct {
int operator()() {
return 42;
}
} get_value;
return get_value();
}
That isn't standard; it's a language extension supported by some compilers.
@@reubenfrench6288 Which part is non-standard? It works with GCC, Clang and MSVC but anyway, that's just a fun experiment, not something you should write. Lambdas are the way to go
@@benjaminnavarro865 Anonymous structs are non-standard, but anonymous unions are.
Anonymous structs aren't allowed, but this is not an anonymous struct. This is an unnamed struct. They are allowed.
@@michaelmahn4373 Interesting. TIL.
To be more readable, I usually make a variable with lambda, then give it to the algorithm:
auto comp = [](const auto key){ return key < 42; };
auto it = std::find(std::begin(v), std::end(v), comp);
Definitely, I do that more and more in real code now, it's also how I teach in my Best Practices classes.
You forgot to mention that 'true' local functions can capture local variables
What a coincidence, I happened to have been reading about the GCC local functions extension a couple days ago thinking “woah, I don’t know anyone who has talked about this”
I probably should have covered extensions... but it's not really a world I live in, for the sake of portability.
it remind me the streams of Bob Ross when there are comments like " 50 % he is going to draw a cabin" and the chat go wild but in Jason case its "50% he is going to write a lambda".
Lambdas are great and all but I do like local functions. C# introduced local functions a few years ago and I really like it (I personally find it looks cleaner).
auto func(Args&&... args) -> ReturnType { }
auto func = [ ](Args&&... args) -> ReturnType { };
pretty much the same in practice, although doing different things. i’d say it’s not a stretch stating that local functions can exist through lambdas, that’s very intuitive...
GCC C compiler have extension Nested-Functions that allow this, interesting they have similar functionality to `[&]` lambda as can reference objects from parent function
You don’t mean the block extension do you?
It's very unsafe though, even if you don't reference stuff from the parent's scope.
@@platin2148 GCC’s local functions extension is different from (and, I think, predates) the block extension.
@@platin2148 blocks is clang's extension and is much closer to actual lambda functions we have in C++. It has different syntax from normal functions (somewhat similar to function pointer syntax), the data is stored either statically or on the heap, and the function part is just treated like a normal function, so there are no security issues like with GCC's Nested Functions.
Nested Functions in GCC have the same syntax as normal functions and basically store a closure in the current stack frame and make the stack executable (which is why they're dangerous)
@@embeddor2230 What is unsafe about it? All I've read is that you can take the address and call if from anywhere, accessing stack variables after they're out of scope. Which is dangerous but it's as expected as referencing any local variable after its scope.
there are some languages that do support local functions (e.g., some dialects of Pascal) but. i find C++ lambdas a superior alternative as can specify the exact manner of closure over outer scope variables of the host function body - which is ay safer than just some sort of defaulting to closure over every outer scope variable in some generic manner of semantics - C++ allows specifying either value or reference semantics
once something resembling local functions exist it really needs to have offer up control over manner of outer scope closure or else is just another gun that will end up shooting a foot off
Except that in Pascal you can store function pointer to capturing "lambda" and in C++ you can't, which I think makes them inferior to Pascal ones. You can also have a non-capturing nested functions that will have access to all previously declared variables in the parent function. I find funny that you are commenting about shooting a foot off, because C++ is literally the number one language in that regard.
nice, Jason, please talk to us furthermore aka dont stop this
None of your non-lambda solutions attempted to use variables in their defining scope act as globals. Fortran statement functions definitely did this. I don't remember about Pascals nested functions. The lambda capture also solves this problem.
I'm wondering what the difference even is, besides syntax, between lambdas and local functions.
What is the point of allowing local function declarations then, if they cannot be defined?
They can be defined, but only in the surrounding scope. For example:
int main() {
void foo();
foo();
}
void foo() {}
I still find little point to this, especially with the tradeoff that allowing this is what gives rise to MVP, but it isn't a dead end.
I don’t think there was ever a point to it, per se. Allowing it was just a side-effect of the grammar, and there was no reason to explicitly disallow it.
I used lambda that way since they are available.
Lambdas are cool. I use them quite a bit for things I need to do more than once inside a method.
Boost Lambda could do this in C++98..... using template meta programming...
The lack of local functions bugged me a lot coming from pascal to different languages. Fortunately most languages fixed this with the introduction of lambdas
Why did you suggest local functions are being considered for C++23? Is it something to do with co-routines or immediate functions?
I don't actually recall suggesting that.
And if I understood right, these lambas's captures and their states are persistent, so even though we may exit the function scope, upon next entry to the function the lamba and reuse its captured captures states.
If you capture by reference, you must be careful to ensure that lifetimes of the referenced variables don't end. Calling a lambda with a dangling reference results in undefined behavior. If you capture by value, the captured values do stay with the lambda as long as it exists.
@@X_Baron cool.
Is there any advantage for local functions compared to lambdas?
Well, in C++ local functions don't really exist, so your main option is to use a lambda, or to emulate one with a local class, like I demonstrate.
Haven't watched yet, but I just wanted to pause and start out by saying that I LOVE embedding unexpected things in function scopes. It makes people so angry!
"We are not allow to use a local class as a tamplate parameter... what a pain in the rear"
jason turner at his finest
You have no idea how many times I used to get that error back when I was first learning C++ and trying to take advantage of algorithms!
Yeah... Allowing the call operator to be static seems like a bad idea that would break everything and only be useful for implementing bad ideas....
I mean I'm open to hearing examples, but to be honest that's just because I'm certain to get a laugh out of it.
Local functions(and lambdas) are overused very often. Separate functions with good names makes code better to read and understand.
That's what local functions do. They separate something out into a new function with its own name that a) doesn't clutter the surrounding scope if it's only used in one place; and b) doesn't require a bunch of boilerplate to pass in local state.
@@Qazqi But still, separate functions make code better to read and understand because you are not littering the main function.
It can go both ways. Having a bunch of single-use functions that are in global scope can also make code harder to reason about. The hard part is finding the mix that works best.
Jason has a little lambda