An abbreviated lambda syntax

1,203 views
Skip to first unread message

Barry Revzin

unread,
Oct 11, 2016, 1:32:57 PM10/11/16
to ISO C++ Standard - Future Proposals
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case. 

Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself. 

abbrev_lambda.html

Richard Smith

unread,
Oct 11, 2016, 1:48:46 PM10/11/16
to std-pr...@isocpp.org
On Tue, Oct 11, 2016 at 10:32 AM, Barry Revzin <barry....@gmail.com> wrote:
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case. 

Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself. 

Seems like an interesting approach.

Minor correction to your "Effects on Existing Code" section: the character sequence => can appear in legal C++ programs today. For instance, X<&Y::operator=>. That said, I've searched a large codebase for this (>100MLoC) and the only occurrence of => (outside of comments, string literals, etc.) was in a compiler test suite.

szollos...@gmail.com

unread,
Oct 11, 2016, 4:37:41 PM10/11/16
to ISO C++ Standard - Future Proposals
Hi,

Also, some corner cases like default argument values and variables already defined are on the list you might want to address. Like `auto x = x => x => x =>x;` and `auto x = x => x = x => x = x;` are non-obvious to me. (`Is = x` an assignment / initialization in caller (later called) code, or a default parameter value?)
My wishlist entry is support for short nullary lambdas in addition to these.

Thanks,
-lorro

Barry Revzin

unread,
Oct 11, 2016, 5:46:15 PM10/11/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Minor correction to your "Effects on Existing Code" section: the character sequence => can appear in legal C++ programs today. For instance, X<&Y::operator=>. That said, I've searched a large codebase for this (>100MLoC) and the only occurrence of => (outside of comments, string literals, etc.) was in a compiler test suite.
 
Ahh, figures it'd have to show up somewhere. Still, that's pretty good.

Also, some corner cases like default argument values and variables already defined are on the list you might want to address. Like `auto x = x => x => x =>x;` and `auto x = x => x = x => x = x;` are non-obvious to me. (`Is = x` an assignment / initialization in caller (later called) code, or a default parameter value?)
My wishlist entry is support for short nullary lambdas in addition to these.

I think you'd have to be maximally greedy in scoping these. So parsing them as:

auto x = x => x => x => x;

auto x = [](auto&& x){
   
return [](auto&& x){
       
return [](auto&& x){
           
return x;
       
};
   
};
};

 and

auto x = x => x = x => x = x;

auto x = [](auto&& x){
   
return x = [](auto&& x){
       
return x = x;
   
};
};

Nullary lambdas fall out naturally too:

auto just5 = () => 5;

Default arguments would have to be parenthesized.

auto maybe5 = (x=5) => x;


Richard Smith

unread,
Oct 11, 2016, 5:53:55 PM10/11/16
to std-pr...@isocpp.org, szollos...@gmail.com
This will be difficult to parse. Requiring lookahead past the ) whenever we see a ( is likely not going to be acceptable. Handling "(identifier-list) =>" would already be a rather special case, but is probably manageable because you can defer handling the identifier-list part until after you reach the ")" and see whether there's a "=>" next.  But once you add in default arguments / assignment-expressions (where the rules for valid expressions are subtly different, especially since earlier parameter names are in scope in the default arguments of later parameters), you can't use that same strategy.

Andrey Semashev

unread,
Oct 11, 2016, 5:56:19 PM10/11/16
to std-pr...@isocpp.org
IMHO, this new syntax makes the code more obscure. The proposed
abbreviated lambdas no longer have any resemblence with functions and
blend in the surrounding code too much (yes, this is a bad thing).

Consider this slightly modified example from the proposal:

int sum = 0;
for (int i = 0; i < 100; ++i)
{
sum += accumulate(ints(i) |
transform(i => i*i) | take_while(i => i < 1000));
}

Can you immediately tell what is i in each occurrence? Can a novice
programmer? What are the structural blocks (scopes) for each meaning of i?

While I'm in general in favor of shorter (yet descriptive!) names, this
makes C++ look more like Perl, which is sometimes called "the language
that you write code in but never read".

Zhihao Yuan

unread,
Oct 11, 2016, 6:59:47 PM10/11/16
to std-pr...@isocpp.org
On Tue, Oct 11, 2016 at 12:48 PM, Richard Smith <ric...@metafoo.co.uk> wrote:
>
> Minor correction to your "Effects on Existing Code" section: the character
> sequence => can appear in legal C++ programs today. For instance,
> X<&Y::operator=>. That said, I've searched a large codebase for this
> (>100MLoC) and the only occurrence of => (outside of comments, string
> literals, etc.) was in a compiler test suite.

A syntax I raised somewhere else is

transform(i = return i * i)

"= return" should have been legal nowhere, and the syntax is
more friendly to my mental model, where "=>" is to introduce
names to types (as in Haskell)...

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://blog.miator.net/

Nicol Bolas

unread,
Oct 11, 2016, 8:18:50 PM10/11/16
to ISO C++ Standard - Future Proposals
On Tuesday, October 11, 2016 at 1:32:57 PM UTC-4, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case. 

Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself. 


I really don't like the idea of a naked `(x)` having a completely different meaning when it's followed by `=>` than when it isn't. And that goes double for any `x`.

People have long-since wanted a terse lambda syntax that doesn't require you to write typenames for variables. I understand that there are some parsing issues related to this concept, but if those can be worked out, I'd be fine with `[](x, y, ...z)` translating to `[](auto &&x, auto &&y, auto... &&z)`.

Given that, I would be fine with `=>` meaning "the following expression will be returned with a noexcept specification based on the expression and decltype(auto) return type deduction".

Tomasz

unread,
Oct 12, 2016, 12:13:31 AM10/12/16
to ISO C++ Standard - Future Proposals
I would like to suggest exstending the => expr to be valid replacement for the function body for any kind of function. I mean that:
template<typename C>
auto begin(C&& c) => std::forward<C>(c).begin();

Would be valid definition, equivalent to:
template<typename C>
inline auto begin(C&& c)
  noexcept(std::forward<C>(c).begin())
    -> decltype(std::forward<C>(c).begin())
{ return std::forward<C>(c).begin(); }

The following restriction will apply:
1) Function with expression-body cannot be forward declared.
2) Explicit inline
4) Can have only auto introducer

As this will use decltype(expr) deduction (not decltype(auto)) it will make the function SFINAE'able on the function body, and will basically resolve problems listed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html paper.

Nicol Bolas

unread,
Oct 12, 2016, 1:09:19 AM10/12/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 12:13:31 AM UTC-4, Tomasz wrote:
I would like to suggest exstending the => expr to be valid replacement for the function body for any kind of function. I mean that:
template<typename C>
auto begin(C&& c) => std::forward<C>(c).begin();

Would be valid definition, equivalent to:
template<typename C>
inline auto begin(C&& c)
  noexcept(std::forward<C>(c).begin())
    -> decltype(std::forward<C>(c).begin())
{ return std::forward<C>(c).begin(); }

The following restriction will apply:
1) Function with expression-body cannot be forward declared.
2) Explicit inline
4) Can have only auto introducer

As this will use decltype(expr) deduction (not decltype(auto)) it will make the function SFINAE'able on the function body, and will basically resolve problems listed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html paper.

But returning `decltype(expr)` is (SFINAE-aside) equivalent to `auto`. Which means that it will always return by value. So if `expr` results in a reference... too bad; it's returning a value now.

To me, the point of the `=>` syntax is to say "perfectly forward and return this expression as much as possible as if you had written it in-situ". Returning `decltype(expr)` won't do that. But returning `decltype((expr))` would.

Other than that, I'm fine with extending `=>` to functions in general.

Tomasz

unread,
Oct 12, 2016, 1:24:29 AM10/12/16
to ISO C++ Standard - Future Proposals
W dniu środa, 12 października 2016 07:09:19 UTC+2 użytkownik Nicol Bolas napisał:
On Wednesday, October 12, 2016 at 12:13:31 AM UTC-4, Tomasz wrote:
I would like to suggest exstending the => expr to be valid replacement for the function body for any kind of function. I mean that:
template<typename C>
auto begin(C&& c) => std::forward<C>(c).begin();

Would be valid definition, equivalent to:
template<typename C>
inline auto begin(C&& c)
  noexcept(std::forward<C>(c).begin())
    -> decltype(std::forward<C>(c).begin())
{ return std::forward<C>(c).begin(); }

The following restriction will apply:
1) Function with expression-body cannot be forward declared.
2) Explicit inline
4) Can have only auto introducer

As this will use decltype(expr) deduction (not decltype(auto)) it will make the function SFINAE'able on the function body, and will basically resolve problems listed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html paper.

But returning `decltype(expr)` is (SFINAE-aside) equivalent to `auto`. Which means that it will always return by value. So if `expr` results in a reference... too bad; it's returning a value now.

No, it does not. decltype(auto) and decltype(expr) are equivalent if the function return statement is return expr, and the only situation when it returns by value, is when expr is actually an prvalue, in which case returnning by value is only way to avoid dangling references.

And live example:
http://melpon.org/wandbox/permlink/u3oE3UCBrVHivK12#

Tomasz

unread,
Oct 12, 2016, 1:27:29 AM10/12/16
to ISO C++ Standard - Future Proposals

Zhihao Yuan

unread,
Oct 12, 2016, 1:47:19 AM10/12/16
to std-pr...@isocpp.org
On Wed, Oct 12, 2016 at 12:24 AM, Tomasz <toma...@gmail.com> wrote:
>> But returning `decltype(expr)` is (SFINAE-aside) equivalent to `auto`.
>> Which means that it will always return by value. So if `expr` results in a
>> reference... too bad; it's returning a value now.
>
>
> No, it does not. decltype(auto) and decltype(expr) are equivalent if the
> function return statement is return expr, and the only situation when it
> returns by value, is when expr is actually an prvalue

My concern is the reversed: user might really want to return
a prvalue using `auto` rules rather than "accidentally" returning
a reference. By default, lambda uses `auto` rules, so making
the abbrev form using `decltype(expr)` changes the intuition.

So here is my suggestion made to applying this change to
normal function definitions by examples:

1. auto f(/* ... */) = return expr;

is equivalent to

auto f(/* ... */) noexcept(expr) -> std::decay_t<decltype(expr)>
{ return expr; }

2. decltype(auto) f(/* ... */) = return expr;

is equivalent to

auto f(/* ... */) noexcept(expr) -> decltype(expr)
{ return expr; }

3. R f(/* ... */) = return expr;

is equivalent to

R f(/* ... */) noexcept(expr)
{ return expr; }

If we follow these conventions as well in abbrev lambdas,
we could optionally let the abbrev form accept a return
type in syntax:

a -> auto = return expr;
a -> decltype(auto) = return expr;
a -> R = return expr;

and

a = return expr;

would yield the first one.

Nicol Bolas

unread,
Oct 12, 2016, 2:06:49 AM10/12/16
to ISO C++ Standard - Future Proposals

But what of lambdas? By default, they use `auto` return mechanics, so `[](...) = return expr` would use the default. But most of the time for such functions, you want decltype(expr) mechanics. That forces you to do `[](...) -> decltype(auto) = return expr`

The point of the feature (as it relates to lambdas) is to make the common case less verbose. Sticking that `decltype(auto)` in there isn't that. Nor is having to use `return` explicitly.

How about this. We use the suggested `=> expr` syntax. By default, it will use `decltype(expr)`. However, we can force `std::decay_t<decltype(expr)>` by saying `=> auto expr`.

The syntax could cause problems if your expression declares a variable. But we could require that `expr` not declare variables (after all, it wouldn't be very useful).

It would be a bit inconsistent, since lambdas normally default to `auto` rather than `decltype(auto)`. But considering that we're using different syntax, it would probably work out OK.

Zhihao Yuan

unread,
Oct 12, 2016, 3:40:04 AM10/12/16
to std-pr...@isocpp.org
On Wed, Oct 12, 2016 at 1:06 AM, Nicol Bolas <jmck...@gmail.com> wrote:
> But most of the time for such functions, you want decltype(expr) mechanics.

For non-capturing cases, maybe. But in general, I can't say
much without data.

> We use the suggested `=> expr` syntax.

Some little research shows that:

Languages using => in lambdas:

JavaScript
C#
D
Dart
Standard ML
Perl 6
Scala

Languages using -> in lambdas:

Erlang
Haskell
OCaml
Java
Julia
F#
Elixir
Swift

I'm glad to see that F# and OCaml "fixed" Standard ML's syntax
here :)

I have a whole bag of reasons about not to use "=>" in
lambdas if we have only bikeshedding left.

> By default, it will
> use `decltype(expr)`. However, we can force `std::decay_t<decltype(expr)>`
> by saying `=> auto expr`.
>
> [...]
>
> It would be a bit inconsistent, since lambdas normally default to `auto`
> rather than `decltype(auto)`. But considering that we're using different
> syntax, it would probably work out OK.

You may have noticed that in terms of teachability (yes, it
should be a word if there is a "C++ discussion mode" in
email clients), you are adding complexity on top of
inconsistency, doubling the work plus fearing people
away...

szollos...@gmail.com

unread,
Oct 12, 2016, 3:52:44 AM10/12/16
to ISO C++ Standard - Future Proposals
Hi,

Can't we just divert `:` for this? I know that '?:' is an operator in itself, but what it tries to be is a definition of two nullary functions and a choice operator (that is not simply choosing between the two, think of a? b? x : y : z and a? x : b? y : z). As a mind game, we could have:
binary a : b equivalent to make_function_pair( () => a>>, () => b>> );
unary a : (or :a, but then take care about the gcc a ?: b) equivalent to () => a>>.
unary (auto&& i) a + i : equivalent to (i) => a>> + i>>.
and the corresponding binary version. If you don't like typing `(auto&& i)`, we could have `(@i)`, with the special case `@i` for a single parameter. `@` is not yet used in C++ and it's widely available due to email addresses. For those who don't have it, one might introduce a digraph like %<.

Note that bindings are mutable and the pairs can be heterogeneous. It's the ? sequence that makes them homogeneous.

Thanks,
-lorro

Zhihao Yuan

unread,
Oct 12, 2016, 4:25:45 AM10/12/16
to std-pr...@isocpp.org
On Wed, Oct 12, 2016 at 2:40 AM, Zhihao Yuan <z...@miator.net> wrote:
>
> I have a whole bag of reasons about not to use "=>" in
> lambdas if we have only bikeshedding left.
>
>> By default, it will
>> use `decltype(expr)`. However, we can force `std::decay_t<decltype(expr)>`
>> by saying `=> auto expr`.
>>
>> [...]
>>
>> It would be a bit inconsistent, since lambdas normally default to `auto`
>> rather than `decltype(auto)`. But considering that we're using different
>> syntax, it would probably work out OK.
>
> You may have noticed that in terms of teachability (yes, it
> should be a word if there is a "C++ discussion mode" in
> email clients), you are adding complexity on top of
> inconsistency, doubling the work plus fearing people
> away...

And here are my constructive notes:

1. If people insist that we must not expand an abbrev
syntax to allow specifying an optional return type,
we have to choose from one deduction rule from the
two, and letting the other one suffers for their own
reasons. Because if we don't want to allow tweaking
a well-known thing, following the same logic we should
not allow tweaking a less well-known thing for a
feature striving for simplicity.

2. If people don't like 'return', here is another syntax
I can think of:

[] -> expr
[] a -> expr
[&cap] a, b -> expr

Can someone tell me that whether it's parseable? I can
read code like this though.

Victor Dyachenko

unread,
Oct 12, 2016, 5:35:32 AM10/12/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
On Wednesday, October 12, 2016 at 12:53:55 AM UTC+3, Richard Smith wrote:

This will be difficult to parse. Requiring lookahead past the ) whenever we see a ( is likely not going to be acceptable. Handling "(identifier-list) =>" would already be a rather special case, but is probably manageable because you can defer handling the identifier-list part until after you reach the ")" and see whether there's a "=>" next.  But once you add in default arguments / assignment-expressions (where the rules for valid expressions are subtly different, especially since earlier parameter names are in scope in the default arguments of later parameters), you can't use that same strategy.
I think anything that creates parsing issues just has to be just disallowed. Particularly hardly ever people need default arguments in this terse syntax. Who needs advanced features always can use "good old" syntax.

TONGARI J

unread,
Oct 12, 2016, 10:00:57 AM10/12/16
to ISO C++ Standard - Future Proposals
If we need the "return" keyword, it's not that terse, you know...
I'd be much happier if we could have the syntax as:
auto f(/* ... */) = expr;

However, one thing that prevents us from pursuing such syntax for normal functions is pure virtual function...

Nicol Bolas

unread,
Oct 12, 2016, 10:04:15 AM10/12/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 3:40:04 AM UTC-4, Zhihao Yuan wrote:
On Wed, Oct 12, 2016 at 1:06 AM, Nicol Bolas <jmck...@gmail.com> wrote:
> But most of the time for such functions, you want decltype(expr) mechanics.

For non-capturing cases, maybe.  But in general, I can't say
much without data.

The cases which motivate the initial proposal need to use the `decltype(expr)` mechanics. Indeed, as outlined by the OP, that's a good 50% of the point of asking for the functionality.

The goal of the syntax is to make it possible to write trivial forwarding functions and expression functions. Using `auto` return deduction works against that, since it doesn't forward return values exactly as if you had written the expression locally.

> By default, it will
> use `decltype(expr)`. However, we can force `std::decay_t<decltype(expr)>`
> by saying `=> auto expr`.
>
> [...]
>
> It would be a bit inconsistent, since lambdas normally default to `auto`
> rather than `decltype(auto)`. But considering that we're using different
> syntax, it would probably work out OK.

You may have noticed that in terms of teachability (yes, it
should be a word if there is a "C++ discussion mode" in
email clients), you are adding complexity on top of
inconsistency, doubling the work plus fearing people
away...

It's only difficult to teach if people need to use the `auto` return type deduction rules.

Domen Vrankar

unread,
Oct 12, 2016, 3:30:24 PM10/12/16
to std-pr...@isocpp.org
2016-10-11 23:56 GMT+02:00 Andrey Semashev <andrey....@gmail.com>:
On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.

Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.

IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).

+1

While I would like  a more compact syntax for lambdas my eyes seem to be really adjusted to brackets so I would prefer such syntax:

(x) => {i < 1000}

So mandatory parentheses around parameters even if there is only a single one and mandatory curly braces around function body even though it is a one liner. Don't care too much about return statement,  semicolon or [ ] brackets in this case - in fact I even prefer them gone.

I believe that this would make it far more readable with not too much extra noise with more resemblance with functions and lambdas. (I hope that it doesn't add issues with initializer lists or something like that...)

Regards,
Domen

Zhihao Yuan

unread,
Oct 12, 2016, 3:36:23 PM10/12/16
to std-pr...@isocpp.org
On Wed, Oct 12, 2016 at 2:29 PM, Domen Vrankar <domen....@gmail.com> wrote:
>
> (x) => {i < 1000}
>
> So mandatory parentheses around parameters even if there is only a single
> one and mandatory curly braces around function body even though it is a one
> liner. Don't care too much about return statement, semicolon or [ ]
> brackets in this case - in fact I even prefer them gone.

Read [] as the introductory λ notation:

[] x -> x < 1000

Totally reassembles the Haskell style

\x -> x < 1000

or the F# and OCaml one

fun x -> x < 1000

Tomasz

unread,
Oct 12, 2016, 3:38:48 PM10/12/16
to ISO C++ Standard - Future Proposals
And having [] is the only way to specify capture for the shorter lambda, like:
[x] y => y + x;

Richard Smith

unread,
Oct 12, 2016, 3:39:15 PM10/12/16
to std-pr...@isocpp.org
I think it's a big mistake to use -> as both a specifier for a return type and for a produced value. People write

  [] -> T { ... }

(missing out the "()") often enough that several implementations have dedicated custom errors to catch this, and there was a proposal to allow it.

Tony V E

unread,
Oct 12, 2016, 4:11:00 PM10/12/16
to Zhihao Yuan
Nothing (exaggeration) else about C++ resembles Haskell, why start now?

I'd prefer it to resemble C++.

Sent from my BlackBerry portable Babbage Device
  Original Message  
From: Zhihao Yuan
Sent: Wednesday, October 12, 2016 3:36 PM
To: std-pr...@isocpp.org
Reply To: std-pr...@isocpp.org
Subject: Re: [std-proposals] An abbreviated lambda syntax
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGsORuAEWp%2BwsUvEZqR%2BTm-LtTKBR-9o5npd1eoZE6dWmD4kMg%40mail.gmail.com.

Nicol Bolas

unread,
Oct 12, 2016, 4:28:02 PM10/12/16
to ISO C++ Standard - Future Proposals


On Wednesday, October 12, 2016 at 3:30:24 PM UTC-4, Domen Vrankar wrote:
2016-10-11 23:56 GMT+02:00 Andrey Semashev <andrey....@gmail.com>:
On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.

Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.

IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).

+1

While I would like  a more compact syntax for lambdas my eyes seem to be really adjusted to brackets so I would prefer such syntax:

(x) => {i < 1000}

Hey; that sounds really close to something good. I would still require the lambda inducer `[]` syntax, but this even permits us to declare what kind of return type deduction it is:

[](...) => auto {expr};

[](...) => {expr}; //uses `decltype(auto)`.

Nicol Bolas

unread,
Oct 12, 2016, 4:29:59 PM10/12/16
to ISO C++ Standard - Future Proposals


On Wednesday, October 12, 2016 at 3:36:23 PM UTC-4, Zhihao Yuan wrote:
On Wed, Oct 12, 2016 at 2:29 PM, Domen Vrankar <domen....@gmail.com> wrote:
>
> (x) => {i < 1000}
>
> So mandatory parentheses around parameters even if there is only a single
> one and mandatory curly braces around function body even though it is a one
> liner. Don't care too much about return statement,  semicolon or [ ]
> brackets in this case - in fact I even prefer them gone.

Read [] as the introductory λ notation:

  [] x -> x < 1000

That imposes a parsing issue.

Whether we have this short lambda syntax or not, we will still need `[]funcname` to be a lambda which calls a function overload set. So the abbreviated lambda syntax should not interfere with that. Without the parenthesis, it's difficult to tell whether `[]x` is supposed to be an overload set or a lambda with one `auto&&` variable.

Tony V E

unread,
Oct 13, 2016, 3:26:06 AM10/13/16
to ISO C++ Standard - Future Proposals
Is [](funcname) about to be valid syntax?

It would be odd to have a place where extraneous () around an identifier are _not_ allowed.

Sent from my BlackBerry portable Babbage Device
From: Nicol Bolas
Sent: Wednesday, October 12, 2016 4:30 PM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] An abbreviated lambda syntax

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

szollos...@gmail.com

unread,
Oct 13, 2016, 3:42:24 AM10/13/16
to ISO C++ Standard - Future Proposals
Hi,

Note that `x` and `(x)` already has very different meanings when it comes to ADL, referenceness, to name a few.

Thanks,
-lorro

Giovanni Piero Deretta

unread,
Oct 13, 2016, 5:13:29 AM10/13/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 12, 2016 at 8:30:24 PM UTC+1, Domen Vrankar wrote:
2016-10-11 23:56 GMT+02:00 Andrey Semashev <andrey....@gmail.com>:
On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.

Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.

IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).

+1


-1 . I think that the syntax as suggested by the OP is quote good.

<rant> Terser syntaxes keep getting rejected from C++ because it might be initially confusing, but then we will be stuck for the next 20 years with the verbose syntax (I can't be the only one that is tired of typing template<typename> over and over), which is a greater impediment to readability as the signal is lost in the noise. Many languages have significantly terser syntax than C++ with no issues. </rant>

Andrey Semashev

unread,
Oct 13, 2016, 5:22:56 AM10/13/16
to std-pr...@isocpp.org
On 10/13/16 12:13, Giovanni Piero Deretta wrote:
>
> <rant> Terser syntaxes keep getting rejected from C++ because it might
> be initially confusing, but then we will be stuck for the next 20 years
> with the verbose syntax (I can't be the only one that is tired of typing
> template<typename> over and over), which is a greater impediment to
> readability as the signal is lost in the noise. Many languages have
> significantly terser syntax than C++ with no issues. </rant>

As I wrote in my (cut away) reply, I'm all for shorter syntax but only
when it's descriptive. Replacing everything with random tokens doesn't
help descriptiveness although it requires less typing.

BTW, I have no problem with template<typename> at all. Just use a better
editor.

Domen Vrankar

unread,
Oct 13, 2016, 5:56:11 AM10/13/16
to std-pr...@isocpp.org
I also have a problem with verbose lambda syntax when providing more than one lambda function as a parameter but would still like to easily distinguish a function mutation from other parts of the code. One of the reasons why I dislike allot of other languages is exactly because they quite often trade too much readability for less key strokes and unfortunately I fix/extent/reuse old code authored by me or often other people far more often than write new code... There is always a trade off between terse syntax and readability and I doubt that 4 additional brackets make a difference in the terse department - from where I stand the biggest verbosity with my lambda functions comes from "auto" or explicit types in parameter list and not from braces.

Regards,
Domen

Victor Dyachenko

unread,
Oct 13, 2016, 6:14:18 AM10/13/16
to ISO C++ Standard - Future Proposals
There is more general issue in the modern C++:

inline void f()
{
   
...
}

is actually

inline void f() noexcept(false)
{
   
...
}

Why it is forbidden to compiler deduce noexcept-ness even for inline functions? It is nonsense and confusing.

Domen Vrankar

unread,
Oct 13, 2016, 6:16:11 AM10/13/16
to std-pr...@isocpp.org

Even with the long example from the proposal that is using noexcepti with only one parameter with auto&& removing brackets would not make much difference while on the other hand preseving brackets and removing the rest makes a huge difference:

// OK: calls #1 invoke([](auto&& x) noexcept(noexcept(test(std::forward<decltype(x)>(x)))) -> decltype(test(std::forward<decltype(x)>(x))) { return test(std::forward<decltype(x)>(x)); }); 

invoke(x => test(forward(x)));
or
invoke([](x) => {test(forward(x))});

Is not that different and I see the later as a good compromise between the two.

Terser syntax yes, removing everything so that it blurs with the surrounding not so much.

Regards,
Domen

Andrey Semashev

unread,
Oct 13, 2016, 6:37:10 AM10/13/16
to std-pr...@isocpp.org
On 10/13/16 13:15, Domen Vrankar wrote:
> 2016-10-13 11:55 GMT+02:00 Domen Vrankar <domen....@gmail.com
> <mailto:domen....@gmail.com>>:
>
> Even with the long example from the proposal that is using noexcepti
> with only one parameter with auto&& removing brackets would not make
> much difference while on the other hand preseving brackets and removing
> the rest makes a huge difference:
>
> |// OK: calls #1 *invoke*([](auto&& *x*)
> noexcept(noexcept(test(std::forward<decltype(x)>(x)))) ->
> decltype(test(std::forward<decltype(x)>(x))) { return
> *test*(std::*forward*<decltype(x)>(*x*)); });
>
> |
> |invoke(x => test(forward(x)));
> |
> |or
> |
> |invoke([](x) => {test(forward(x))});
> |
> |
> |
> |Is not that different and I see the later as a good compromise between
> the two.
> |
> |
> Terser syntax yes, removing everything so that it blurs with the
> surrounding not so much.

I think the original problem should be decomposed into several smaller
ones, which can be dealt with separately.

For instance, I would be interested in a proposal that makes the
explicit template argument to std::forward unnecessary. Regardless
lambdas, this alone would be helpful in general.

Another step is to enable compilers deduce noexcept automatically when
possible (which lambdas surely qualify). Again, this would be a
generally useful addition.

Having done just those two you shorten your lambda by more than half:

invoke([](auto&& x) -> decltype(auto)
{ return test(std::forward(x)); });

Further improvements like making auto&& in the lambda argument list
optional are also possible, but more targeted to this particular
problem. They can be made evolutionary.

Nicol Bolas

unread,
Oct 13, 2016, 11:17:15 AM10/13/16
to ISO C++ Standard - Future Proposals
On Thursday, October 13, 2016 at 3:26:06 AM UTC-4, Tony V E wrote:
Is [](funcname) about to be valid syntax?

That all depends on the eventual wording from new versions of the feature. But I would say that it shouldn't. It would create an ambiguity between an overload set and a lambda with members. Even without terse lambda declarations, users can accidentally use the wrong typename something. That's a useful compile error to get.

Nicol Bolas

unread,
Oct 13, 2016, 12:16:11 PM10/13/16
to ISO C++ Standard - Future Proposals
On Thursday, October 13, 2016 at 6:37:10 AM UTC-4, Andrey Semashev wrote:
On 10/13/16 13:15, Domen Vrankar wrote:
> 2016-10-13 11:55 GMT+02:00 Domen Vrankar <domen....@gmail.com
> <mailto:domen....@gmail.com>>:
>
> Even with the long example from the proposal that is using noexcepti
> with only one parameter with auto&& removing brackets would not make
> much difference while on the other hand preseving brackets and removing
> the rest makes a huge difference:
>
> |// OK: calls #1 *invoke*([](auto&& *x*)
> noexcept(noexcept(test(std::forward<decltype(x)>(x)))) ->
> decltype(test(std::forward<decltype(x)>(x))) { return
> *test*(std::*forward*<decltype(x)>(*x*)); });
>
> |
> |invoke(x => test(forward(x)));
> |
> |or
> |
> |invoke([](x) => {test(forward(x))});
> |
> |
> |
> |Is not that different and I see the later as a good compromise between
> the two.
> |
> |
> Terser syntax yes, removing everything so that it blurs with the
> surrounding not so much.

I think the original problem should be decomposed into several smaller
ones, which can be dealt with separately. 
 
For instance, I would be interested in a proposal that makes the
explicit template argument to std::forward unnecessary. Regardless
lambdas, this alone would be helpful in general.

I don't think there is a way to do that. And even if there was, you still have the noise of `std::forward` cluttering up the function.

The last 5-6 years of C++11 have taught us that, in template code, you're going to be forwarding a lot. Given that common pattern, having explicit syntax for doing so is not unreasonable. Granted, I don't like the OP's particular operator suggestion, but I do believe that we need something.

Another step is to enable compilers deduce noexcept automatically when
possible (which lambdas surely qualify). Again, this would be a
generally useful addition.

I don't agree with this.

Noexcept deduction, if we allow it, must be something we ask for on regular function declarations. Why? Because it becomes part of the interface of an API. And yet, it's not actually part of the function's interface. It's something that changes based on the implementation, since it is deduced from the implementation.

Users can detect if an API of yours is noexcept. And if it is, then they can write code that requires this. If it were automatically deduced, then users would have to maintain it even if they didn't intend for the function to be noexcept.

Lambdas are not used as the interface in an API, so allowing them to deduce noexcept may be OK. But this is not something that should generally be imposed on a function.

Having done just those two you shorten your lambda by more than half:

   invoke([](auto&& x) -> decltype(auto)
     { return test(std::forward(x)); });

The problem I have with decomposing the problem the way you do is this.

We have a problem with what should be short, trivial lambdas having a lot of noise. That noise comes from a lot of places. However, unless you eliminate all of them, the result, while smaller, will still be too noisy.

Your above code is certainly an improvement (even though the forward trick can't work). But it's still way too big; the problem is still there. Also, your code doesn't handle SFINAE, since you're using `decltype(auto)` which doesn't do SFINAE on the return expression. That's very important.

The usefulenss of the `=>` syntax is that it solves about 60% of the problem. It solves multiple problems all at once. You can only get such a solution by focusing on the lambda problem itself. If you decompose the problems, you'll get decomposed solutions with them. These may make the syntax shorter, but they won't necessarily fix the problem.

I'm not against decomposition per-se. But any such decomposition needs to remain focused on the specific problems.

1: Parameter declarations in lambdas. Having to specify a type rather than getting `auto&&`.

2: Specifying return types and noexcept explicitly in order to properly return an expression, with SFINAE intact.

3: Specifying std::forward<...> for the use of forwarding reference parameters.

It seems to me that, in terms of code burden, #2 is the one that takes up the most room, followed by #3. #1, while not unimportant, is not as gigantic as the others.

Solving #2 is essentially about having a function whose entire body is "return <expr>;". The `=> expr` syntax discussed in this thread seems like a good fit here, especially since it could also be applied to regular function declarations.

Solving #3... is very tricky without having to introduce a new keyword/operator.

Andrey Semashev

unread,
Oct 13, 2016, 1:45:18 PM10/13/16
to std-pr...@isocpp.org
On 10/13/16 19:16, Nicol Bolas wrote:
> On Thursday, October 13, 2016 at 6:37:10 AM UTC-4, Andrey Semashev wrote:
>
> For instance, I would be interested in a proposal that makes the
> explicit template argument to std::forward unnecessary. Regardless
> lambdas, this alone would be helpful in general.
>
>
> I don't think there is a way to do that.

Not without some sort of compiler magic or a new language feature. But
I'm ok if magic is involved.

> And even if there was, you
> still have the noise of `std::forward` cluttering up the function.

And I like that it's there. It makes the intention more obvious, same as
when you use std::move or std::ref.

> Another step is to enable compilers deduce noexcept automatically when
> possible (which lambdas surely qualify). Again, this would be a
> generally useful addition.
>
>
> I don't agree with this.
>
> Noexcept deduction, if we allow it, /must be/ something we ask for on
> regular function declarations. Why? Because it becomes part of the
> interface of an API. And yet, it's not actually part of the function's
> interface. It's something that changes based on the implementation,
> since it is deduced /from/ the implementation.
>
> Users can detect if an API of yours is noexcept. And if it is, then they
> can write code that requires this. If it were automatically deduced,
> then users would have to maintain it even if they didn't intend for the
> function to be noexcept.

While I agree with the argument that noexcept is part of the interface,
it is too often I just want it to propagate throughout the code. Writing
generic functions and classes, it is indeed too tedious to spell the
correct noexcept condition and keep it in sync with the actual code as
it changes - so tedious that you either give up on noexcept or just
force it and say that otherwise is not supported by your
no-longer-generic code. If it has to be requested explicitly then it
should be something short and simple, like this:

void foo() noexcept(auto)
{
// ...
}

> Lambdas are not used as the interface in an API, so allowing them to
> deduce noexcept may be OK. But this is not something that should
> generally be imposed on a function.

Ok, so we make noexcept(auto) the implicit default for lambdas but not
other functions. I'm ok with that.

> Having done just those two you shorten your lambda by more than half:
>
> invoke([](auto&& x) -> decltype(auto)
> { return test(std::forward(x)); });
>
>
> The problem I have with decomposing the problem the way you do is this.
>
> We have a problem with what should be short, trivial lambdas having a
> lot of noise. That noise comes from a lot of places. However, unless you
> eliminate /all of them/, the result, while smaller, will still be too noisy.
>
> Your above code is certainly an improvement (even though the forward
> trick can't work). But it's still /way/ too big; the problem is still
> there. Also, your code doesn't handle SFINAE, since you're using
> `decltype(auto)` which doesn't do SFINAE on the return expression.
> That's very important.

I just suggested two decoupled changes that would significantly reduce
the noise and would otherwise be useful in other contexts. Note that the
lambda function is still very recognizable and no special syntax is
required. Further improvements are possible, but I'm sure they can also
be applied iteratively and also be useful elsewhere.

> The usefulenss of the `=>` syntax is that it solves about 60% of the
> problem. It solves multiple problems all at once. You can only get such
> a solution by focusing on the lambda problem itself. If you decompose
> the problems, you'll get decomposed solutions with them. These may make
> the syntax shorter, but they won't necessarily fix the problem.

But when you introduce the new syntax you only solving the problem for
lambdas (as I opined earlier, in a not good way) leaving other parts of
the language disconnected.

> I'm not against decomposition per-se. But any such decomposition needs
> to remain focused on the specific problems.
>
> 1: Parameter declarations in lambdas. Having to specify a type rather
> than getting `auto&&`.
>
> 2: Specifying return types and noexcept explicitly in order to properly
> return an expression, with SFINAE intact.
>
> 3: Specifying std::forward<...> for the use of forwarding reference
> parameters.
>
> It seems to me that, in terms of code burden, #2 is the one that takes
> up the most room, followed by #3. #1, while not unimportant, is not as
> gigantic as the others.
>
> Solving #2 is essentially about having a function whose entire body is
> "return <expr>;". The `=> expr` syntax discussed in this thread seems
> like a good fit here, especially since it could also be applied to
> regular function declarations.

I disagree. I mean, you are correct if all you want to do is optimize
the very specific case - a lambda with a single return statement. But my
point is that we should not be optimizing that one single case, when the
same problems exist in other cases as well. Noexcept specifications are
yet more verbose for functions that are more complex than a lazy return.
Callability forwarding (i.e. SFINAE-friendliness) is yet more difficult.
These problems need solving in general, and by induction in lambda
functions.

I don't mind if there are lambda-specific improvements along the road,
just don't invent yet another syntax for a function just for one
specific case. Keep the language consistent.

Victor Dyachenko

unread,
Oct 13, 2016, 2:06:54 PM10/13/16
to ISO C++ Standard - Future Proposals
On Thursday, October 13, 2016 at 7:16:11 PM UTC+3, Nicol Bolas wrote:
Noexcept deduction, if we allow it, must be something we ask for on regular function declarations. Why? Because it becomes part of the interface of an API. And yet, it's not actually part of the function's interface. It's something that changes based on the implementation, since it is deduced from the implementation.

Users can detect if an API of yours is noexcept. And if it is, then they can write code that requires this. If it were automatically deduced, then users would have to maintain it even if they didn't intend for the function to be noexcept.

Lambdas are not used as the interface in an API, so allowing them to deduce noexcept may be OK. But this is not something that should generally be imposed on a function.
How inline functions are different in this sense? How

template<class Func>
inline decltype(auto) wrapper(Func &&f) noexcept(noexcept(std::forward<Func>(f)()))
{
    std
::forward<Func>(f)();
}

is "API-stable"? Why do wee need this crap with noexcept(noexcept(...)) in such cases? Especially with inline move-operations, where noexceptness is critical for performance and exception safety

Nicol Bolas

unread,
Oct 14, 2016, 1:28:10 AM10/14/16
to ISO C++ Standard - Future Proposals
On Thursday, October 13, 2016 at 2:06:54 PM UTC-4, Victor Dyachenko wrote:
On Thursday, October 13, 2016 at 7:16:11 PM UTC+3, Nicol Bolas wrote:
Noexcept deduction, if we allow it, must be something we ask for on regular function declarations. Why? Because it becomes part of the interface of an API. And yet, it's not actually part of the function's interface. It's something that changes based on the implementation, since it is deduced from the implementation.

Users can detect if an API of yours is noexcept. And if it is, then they can write code that requires this. If it were automatically deduced, then users would have to maintain it even if they didn't intend for the function to be noexcept.

Lambdas are not used as the interface in an API, so allowing them to deduce noexcept may be OK. But this is not something that should generally be imposed on a function.
How inline functions are different in this sense? How

template<class Func>
inline decltype(auto) wrapper(Func &&f) noexcept(noexcept(std::forward<Func>(f)()))
{
    std
::forward<Func>(f)();
}

is "API-stable"?

It's perfectly stable. The function does exactly what it says it does, and has the noexcept statement it says it has.

My problem is that changing the implementation changes the interface, without that interface being apparent to anyone reading the code. I can have an API function that is automatically `noexcept` simply because I never called a non-`noexcept` function. But if I suddenly feel the need to call a library function that isn't `noexcept`, my code stops being `noexcept`. And I've broken my API.

An API that I did not ask for. Remember: I was talking about a suggestion for implicit deduction of `noexcept`.

Though I'm not exactly a fan of explicit deduction of `noexcept` for anything more complex than an expression...

Nicol Bolas

unread,
Oct 14, 2016, 2:45:11 AM10/14/16
to ISO C++ Standard - Future Proposals
On Thursday, October 13, 2016 at 1:45:18 PM UTC-4, Andrey Semashev wrote:
On 10/13/16 19:16, Nicol Bolas wrote:
> On Thursday, October 13, 2016 at 6:37:10 AM UTC-4, Andrey Semashev wrote:
>
>     For instance, I would be interested in a proposal that makes the
>     explicit template argument to std::forward unnecessary. Regardless
>     lambdas, this alone would be helpful in general.
>
>
> I don't think there is a way to do that.

Not without some sort of compiler magic or a new language feature. But
I'm ok if magic is involved.

> And even if there was, you
> still have the noise of `std::forward` cluttering up the function.

And I like that it's there. It makes the intention more obvious, same as
when you use std::move or std::ref.

And this is part of my point: by breaking the problem down into such small, disparate pieces, you have effectively designed yourself into a position where you don't actually solve the original problem anymore.

We need to spell out `std::move` and `std::ref` because, in many cases, it's not at all clear when you intend to do that operation. With `std::forward`, there's always a good indicator that you want to use it. Namely, the fact that you're using a forwarding reference. Most of the time, when you use those, you should be using `std::forward` with them.

While it is true that sometimes you don't want to, there are times when you are essentially guaranteed to want to forward. In those cases, it's completely obvious to the reader what you're doing, such that having `std::forward` there is pointless noise that detracts from the code you're trying to write. `std::forward()` is 14 keystrokes that don't need to be there, in an environment where every keystroke matters.

By focusing only on the general cases, you miss the details that make for a good solution to the specific one.
 
If it has to be requested explicitly then it
should be something short and simple, like this:

   void foo() noexcept(auto)
   {
     // ...
   }

> Lambdas are not used as the interface in an API, so allowing them to
> deduce noexcept may be OK. But this is not something that should
> generally be imposed on a function.

Ok, so we make noexcept(auto) the implicit default for lambdas but not
other functions. I'm ok with that.

Since that might potentially break existing code (my suggestion would only apply to the new `=>` syntax, not to already-written lambdas), I can't say I'm OK with that.

> The usefulenss of the `=>` syntax is that it solves about 60% of the
> problem. It solves multiple problems all at once. You can only get such
> a solution by focusing on the lambda problem itself. If you decompose
> the problems, you'll get decomposed solutions with them. These may make
> the syntax shorter, but they won't necessarily fix the problem.

But when you introduce the new syntax you only solving the problem for
lambdas (as I opined earlier, in a not good way) leaving other parts of
the language disconnected.

There is no reason why `=>` syntax, along with its noexcept and return type deduction syntax, could not be applied to named functions. This was discussed earlier in the thread.

So if you have a single-expression function, you can express it like this:

template<typename ...Ts> auto something(Ts &&...ts) => {some_func(ts...)};

Sure, that's not like a huge thing. But it's hardly trivial. And it buys you all of the features of the lambda version.

> I'm not against decomposition per-se. But any such decomposition needs
> to remain focused on the specific problems.
>
> 1: Parameter declarations in lambdas. Having to specify a type rather
> than getting `auto&&`.
>
> 2: Specifying return types and noexcept explicitly in order to properly
> return an expression, with SFINAE intact.
>
> 3: Specifying std::forward<...> for the use of forwarding reference
> parameters.
>
> It seems to me that, in terms of code burden, #2 is the one that takes
> up the most room, followed by #3. #1, while not unimportant, is not as
> gigantic as the others.
>
> Solving #2 is essentially about having a function whose entire body is
> "return <expr>;". The `=> expr` syntax discussed in this thread seems
> like a good fit here, especially since it could also be applied to
> regular function declarations.

I disagree. I mean, you are correct if all you want to do is optimize
the very specific case - a lambda with a single return statement. But my
point is that we should not be optimizing that one single case, when the
same problems exist in other cases as well.

But they don't exist in other cases. Or rather, not in the same way.

The verbosity of reading a function declaration is certainly a real thing. But lambdas are frequently written in-situ, in the middle of expressions. The problem is more important there, and the shorter the lambda's body conceptually could be, the lower the tolerance for noise. If all you're doing is returning `a + 1`, every keystroke beyond that has to be meaningful, a legitimate choice that you pick or don't pick based on need.

So while other cases may be verbose, in the case of the lambda, it is especially pernicious.
 
Noexcept specifications are
yet more verbose for functions that are more complex than a lazy return.
Callability forwarding (i.e. SFINAE-friendliness) is yet more difficult.
These problems need solving in general, and by induction in lambda
functions.

That leads to trickle-down terseness.

Consider what we discussed with automatic noexcept generation. It is unworkable to suddenly change the unspecified noexcept declaration of millions of lambda functions to be automatically deduced. Therefore, the only way that lambdas could get in on that is to specify `noexcept(auto)`. While this would be functional, it would hardly be called "terse". Same goes for `std::forward`: you get a solution that removes some cruft, but that's all.

And in that way, the user never quite gets the lambda terseness they want.

Victor Dyachenko

unread,
Oct 14, 2016, 2:47:59 AM10/14/16
to ISO C++ Standard - Future Proposals
Sadly, noexcept and constexpr quickly become an annoying noise in the language, just like restrict in C99. Not every function is part of "API"! In 90%+ of cases I prefer transparent noexcept-ness without explicit stating this.

What is the problem with noexcept-transparency for inline functions? The whole idea of inline functions is that replacing the expression with inline function doesn't impact generated code - you finally get the same result. It's zero-cost abstractions which C++ is famous for.

Andrey Semashev

unread,
Oct 14, 2016, 6:15:50 AM10/14/16
to std-pr...@isocpp.org
On 10/14/16 09:45, Nicol Bolas wrote:
> On Thursday, October 13, 2016 at 1:45:18 PM UTC-4, Andrey Semashev wrote:
>
> > And even if there was, you
> > still have the noise of `std::forward` cluttering up the function.
>
> And I like that it's there. It makes the intention more obvious,
> same as
> when you use std::move or std::ref.
>
> And this is part of my point: by breaking the problem down into such
> small, disparate pieces, you have effectively designed yourself into a
> position where you don't actually solve the original problem anymore.
>
> We need to spell out `std::move` and `std::ref` because, in many cases,
> it's not at all clear when you intend to do that operation. With
> `std::forward`, there's always a good indicator that you want to use it.
> Namely, the fact that you're using a forwarding reference. Most of the
> time, when you use those, you should be using `std::forward` with them.
>
> While it is true that sometimes you don't want to, there are times when
> you are essentially guaranteed to want to forward. In those cases, it's
> /completely obvious/ to the reader what you're doing, such that having
> `std::forward` there is pointless noise that detracts from the code
> you're trying to write. `std::forward()` is 14 keystrokes that don't
> need to be there, in an environment where every keystroke matters.

I wouldn't say my typical code base is swarmed with forwarding
functions. I'd say, situations when you obviously do want to
std::forward are equally frequent to those when you don't. At least, I
can't say I write or read code with one preference significantly more
often than the other. So I would rather have std::forward spelled out
explicitly every single time when it's intended rather than having to
guess. Of course, it all depends on the kind of code we write, so our
experiences differ.

Consider also that the function call syntax currently is self-sufficient
in a way that you know which arguments you are discarding (i.e. moving
or forwarding) and which you do not. With the new syntax this is no
longer the case since it changes the way function arguments are
interpreted (i.e. implicit forward). I know you're saying that's the
whole point of the change, but think how it will play in functions more
complex than an immediate forward to another function. I don't think
that would improve readability.
Well, let's just say I don't want to trade terseness at the expense of
readability or expressiveness of the code.

Barry Revzin

unread,
Oct 14, 2016, 9:59:39 AM10/14/16
to ISO C++ Standard - Future Proposals

Consider also that the function call syntax currently is self-sufficient
in a way that you know which arguments you are discarding (i.e. moving
or forwarding) and which you do not. With the new syntax this is no
longer the case since it changes the way function arguments are
interpreted (i.e. implicit forward). 

I'll post an updated proposal later, but just want to chime here - there is no implicit forwarding here. The function arguments are interpreted the same way, I just want to allow for omission of the parameter types, the trailing decltype, and the return keyword:

// none of these do any forwarding
[](auto&& x) -> decltype(twice(x)) { return twice(x); }
[](auto&& x) => twice(x);
[](x) => twice(x);

// all of these do forwarding
[](auto&& x) -> decltype(twice(std::forward<decltype(x)>(x))) { return twice(std::forward<decltype(x)>(x)); }
[](auto&& x) => twice(std::forward<decltype(x)>(x));
[](x) => twice(std::forward<decltype(x)>(x));
[](x) => twice(>>x);

You still have to mark the argument as being forwarded.
 

Nicol Bolas

unread,
Oct 14, 2016, 10:53:45 AM10/14/16
to ISO C++ Standard - Future Proposals

Fair enough, but I still hate using `>>` for that ;)

Granted, I don't have a better alternative at the moment...

Barry Revzin

unread,
Oct 14, 2016, 9:33:02 PM10/14/16
to ISO C++ Standard - Future Proposals


On Tuesday, October 11, 2016 at 12:32:57 PM UTC-5, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case. 

Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself. 


Here's an updated draft. I've dropped the version of the syntax that allows for the omission of [] and (). At best, that saved four characters for single-argument no-capture lambdas, which is a small amount of the overall character savings, and seems hardly worth the added complexity in parsing. I actually defined >>expr (which had not been the case before) and extend the proposal to apply the same idea for function templates. 
abbrev_lambda.html

Nicol Bolas

unread,
Oct 14, 2016, 9:51:22 PM10/14/16
to ISO C++ Standard - Future Proposals

Since you're creating a new operator, I would like to have some explanation of the precedence of this operator. I imagine that it would be at precedence level 3, along with the other prefix operators.

You also don't make it clear if the new abbreviated lambda parameter declaration requires the use of `=>` or not. That is, is it legal to do `[](x) {foo(x + 1);}`? Similarly, it isn't completely clear that `>>` can be used outside of the context of terse lambda expressions.

Basically, I think you should make it more clear that there are 3 independent features in one proposal, which all leads to shorter and cleaner code.

szollos...@gmail.com

unread,
Oct 15, 2016, 3:36:23 AM10/15/16
to ISO C++ Standard - Future Proposals
Hi,

We actually do have a syntax for forwarding and delay: it's operator : . But to understand this, we have to start a little further, break down and build up delayed execution to pieces.
First, we (re)define the following constructs:
  • : expr means a lambda expression that captures (by mutable reference) everything in expr and returns expr (forwarded when that makes sense). It's either prefix or infix, the latter captures a pair of lambdas. Thus : a : b equals : ( a : b).
  • R(Ti ai, ...) or R(T... args) or (Ti ai, ...) -> R or (T... args) -> R mean that a stack frame is allocated[1]. Usually this means function definition; for lambda, one might omit R. Note that the human eye parses this, i.e. 'two things separated by space, which might become a list and is in parentheses; it'd be counter-intuitive skipping this, especially since one might `#define A auto&&`.. [3]
  • [...] means a type is being defined which can manually alter what's to capture. [2]
Then we define a ? b as a binary operator (thus we got rid of the ternary) as b().first() or b().second() depending on a. Note that b is also delayed, this is essential for nested ?:. Same logic applies for &&, ||.

Finally, we could allow stack frame[1] definition inside : expr, that is, :(auto&& x) ++i, x; for a lambda takes an x, returns (forwards) it and counts the number of times it was called.

One final step remains. What if you didn't want to forward? Gcc has statement expressions which don't need a return statement but instead return the last expr in body. We might borrow this idea (while still allowing for explicit return). In fact, most of the missing return errors that occur in a function just expect this, so we generalize to sg that was an error before with the intention of the code that resulted in error.
This means, [](auto&& x){ ++i, x; } is allowed and does not forward, while :(auto&& x) ++i, x; does forward.
/* one might even try to remove {} from the first */

What do you think?

Thanks,
-lorro

[1] Technically, we don't necessarily have stack, so it's parameter/argument list on one side and automatic storage duration variables received on the other side.
[2] One should discuss mutable here.
[3] If that's still not enough, one might allow (&& x) for (auto&& x) and (& x) for (auto& x).

Tony V E

unread,
Oct 15, 2016, 11:47:01 AM10/15/16
to ISO C++ Standard - Future Proposals
I keep thinking something like back-tick would be useful in this discussion. 

Like `x + 1` is a 'lifted' or to-be-inlined expression, a lambda where x is auto-forwarded because ‎you don't get a new type at all, just a direct substitution of x.  

Or maybe `x` means forward x. 

(yes, I know, back-tick isn't part of the character set)


Sent from my BlackBerry portable Babbage Device
Sent: Saturday, October 15, 2016 3:36 AM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] An abbreviated lambda syntax
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

szollos...@gmail.com

unread,
Oct 15, 2016, 1:19:02 PM10/15/16
to ISO C++ Standard - Future Proposals
Hi,

Sounds very promising.. would you perhaps allow statements as well? That'd fix my other proposal, capturing return / break / continue: you could say f(i, `return false;`) or f(i, `break;`).
Adding a digraph is no problem :).

Thanks,
-lorro

Barry Revzin

unread,
Oct 15, 2016, 1:47:32 PM10/15/16
to ISO C++ Standard - Future Proposals
Going to reply to a bunch of people in one go:

 
Nicol:
Since you're creating a new operator, I would like to have some explanation of the precedence of this operator. I imagine that it would be at precedence level 3, along with the other prefix operators.

Yes, that would seem about right.  And >> would be usable anywhere. 

 
Nicol:
You also don't make it clear if the new abbreviated lambda parameter declaration requires the use of `=>` or not. That is, is it legal to do `[](x) {foo(x + 1);}`? Similarly, it isn't completely clear that `>>` can be used outside of the context of terse lambda expressions.

One problem that comes up with allowing for omitting the type (and this is as true for the => notation as it is for the normal lambda notation) is a potential ambiguity:

[](arg ) { return sizeof(arg); }

is this a lambda taking a single unnamed variable of type arg and returning that size, or is it a lambda taking a forwarding reference named arg and returning the size of the type it refers to? To allow the terse naming for normal lambdas, it would have to be the latter - but there are undoubtedly lambdas in existence that take unnamed arguments. And this could break code if that code relies on side effects of constructing those arguments. Or at the very least will suddenly start having warnings about unused variables. 

I would like for the terse syntax to be used anywhere, but this case concerns me. 
 
Lorro:
What do you think?

I do not understand your use of : and what it means in the context of lambdas, sorry.

Tony:
Like `x + 1` is a 'lifted' or to-be-inlined expression, a lambda where x is auto-forwarded because ‎you don't get a new type at all, just a direct substitution of x. Or maybe `x` means forward x.

This reminds me a bit of how Scala allows you to do lambdas with _. There you can write:

transform(first, last, _ + 1);
sort
(first, last, _ > _);

to mean:

transform(first, last, [&](auto&& x) { return x+1; });
sort
(first, last, [&](auto&& a, auto&& b) { return a > b; });

I may be wrong about the implicit capture by reference, but at least something to that effect. 

It's easy to come up with examples where this syntax is pretty awesome. But once you get passed exceedingly simple, there are a lot of questions that come up. How do you take one argument and pass it twice to a different function (which means you can't forward it twice)? How do you invert the input arguments? How do you deal with captures of expressions? My version 0.1 of this proposal was based on the _ syntax, but it got buried pretty quickly under a pile of unanswerable questions. 

Nicol Bolas

unread,
Oct 15, 2016, 2:15:57 PM10/15/16
to ISO C++ Standard - Future Proposals


On Saturday, October 15, 2016 at 1:47:32 PM UTC-4, Barry Revzin wrote:
Going to reply to a bunch of people in one go:

 
Nicol:
Since you're creating a new operator, I would like to have some explanation of the precedence of this operator. I imagine that it would be at precedence level 3, along with the other prefix operators.

Yes, that would seem about right.  And >> would be usable anywhere. 

 
Nicol:
You also don't make it clear if the new abbreviated lambda parameter declaration requires the use of `=>` or not. That is, is it legal to do `[](x) {foo(x + 1);}`? Similarly, it isn't completely clear that `>>` can be used outside of the context of terse lambda expressions.

One problem that comes up with allowing for omitting the type (and this is as true for the => notation as it is for the normal lambda notation) is a potential ambiguity:

[](arg ) { return sizeof(arg); }

is this a lambda taking a single unnamed variable of type arg and returning that size, or is it a lambda taking a forwarding reference named arg and returning the size of the type it refers to? To allow the terse naming for normal lambdas, it would have to be the latter - but there are undoubtedly lambdas in existence that take unnamed arguments. And this could break code if that code relies on side effects of constructing those arguments. Or at the very least will suddenly start having warnings about unused variables. 

I would like for the terse syntax to be used anywhere, but this case concerns me.

What should concern you much more is that you can't really make this based on =>. To do that would require lots and lots of look-ahead, and we don't want parsers to have to do that. So whatever the solution will be, it must be generic.

Worst-case scenario, if we can't make a naked identifier work, we can have people use `&&` and `&` (or `const&`/`const&&`). If you want a value parameter, you have to specify a type/`auto`. It's not as short as it could be, but it's still shorter than having to type `auto&&`.

And of course, you can do `...&&args`.

szollos...@gmail.com

unread,
Oct 15, 2016, 3:43:27 PM10/15/16
to ISO C++ Standard - Future Proposals
Hi,


This reminds me a bit of how Scala allows you to do lambdas with _. There you can write:

transform(first, last, _ + 1);
sort
(first, last, _ > _);

to mean:

transform(first, last, [&](auto&& x) { return x+1; });
sort
(first, last, [&](auto&& a, auto&& b) { return a > b; });

I may be wrong about the implicit capture by reference, but at least something to that effect.

We used to have this with std::placeholders::_1 , _2, .. & bind() and I'm glad we can replace it. Naming your argument correctly means you're halfway there. search(needle, haystack) looks way better than search(_1, _2). (which is which?)
One thing you might do - and what a part of the community will continuously oppose - is to simply not declare the arguments. Granted, this requires passing arguments by name - something that's due for a long time now; but then, any variable that's undefined (or, if you wish, any captured or undefined variable) is automatically assumed to be auto&&. This requires something like `expr` to differentiate from normal functions.

Thanks,
-lorro

szollos...@gmail.com

unread,
Oct 15, 2016, 5:40:57 PM10/15/16
to ISO C++ Standard - Future Proposals
Lorro:
What do you think?

I do not understand your use of : and what it means in the context of lambdas, sorry.
TL;DR version is, :x+1 equals [&]`x + 1`; furthermore, y+1 : x+1 equals z such that z.first equals [&]`y+1` and z.second equals [&]`x+1`.
You can define ?:, &&, || in terms of the above; in fact, this is derived from what ?: is. You might let :(auto&& x) x+1 into the syntax - or any of the abbrev.s discussed.
You can't really get shorter than this, because this is just one character expressing this all :). Furthermore, it's in line with what ?: is now.

Thanks,
-lorro

Nicol Bolas

unread,
Oct 15, 2016, 6:28:08 PM10/15/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com

Except that's not what ?: does. If your back-tick things create lambdas, then that's very different from how ?: works. And if your back-tick things don't create lambdas, then it's not clear what's going on.

Vicente J. Botet Escriba

unread,
Oct 16, 2016, 1:35:10 PM10/16/16
to std-pr...@isocpp.org
Hi,

Have you considered the same thing for functions?

auto fct(auto&& x) => test(x);
shall be exactly equivalent to the function:
auto fct(auto&& x) noexcept(noexcept(test(x))) -> decltype(test(x)) {
    return test(x);
}
Vicente

Nicol Bolas

unread,
Oct 16, 2016, 2:56:19 PM10/16/16
to ISO C++ Standard - Future Proposals

Yes, he considered that. It's right there in the document he posted ;) Search for "Abbreviated Function Syntax".

Barry Revzin

unread,
Oct 16, 2016, 3:28:47 PM10/16/16
to ISO C++ Standard - Future Proposals


Yes, he considered that. It's right there in the document he posted ;) Search for "Abbreviated Function Syntax".

Yep! Although here's a rewritten version where I'm clarifying that there are three independent proposals here: => expr, unary>>, and omitting types in lambdas. Hopefully this'll be clearer that the intent of => is to be used in both lambdas and functions. 
abbrev_lambda.html

Ville Voutilainen

unread,
Oct 16, 2016, 3:43:03 PM10/16/16
to ISO C++ Standard - Future Proposals
Except for unary>>, these features were proposed and rejected in
Portland. I don't see much in this
proposal that would make the objections made at that time moot.

Nicol Bolas

unread,
Oct 16, 2016, 3:48:12 PM10/16/16
to ISO C++ Standard - Future Proposals

Could you provide some information on what those proposals were and what the objections were?

Tomasz

unread,
Oct 16, 2016, 3:53:19 PM10/16/16
to ISO C++ Standard - Future Proposals
W dniu niedziela, 16 października 2016 21:43:03 UTC+2 użytkownik Ville Voutilainen napisał:

Except for unary>>, these features were proposed and rejected in
Portland. I don't see much in this
proposal that would make the objections made at that time moot.

For => expr, I belive you are reffering to section 2.3 of  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf. However, the proposal there, was to allow
[] () expr
With semantics equivalent to:
[] () { return expr; }
Notice that it still use auto return type deduction (no SFINAE) and returns by value.

The => expr is generalized to be expression body syntax, that will be available for all functions (including lambda) and will be equivalent to:
noexcept(noexcept(expr))
  -> decltype(expr)
{ return expr; }

This basically resolve the problem of uncessary repetition and also partially resolves problem with return type deduction and SFINAE (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html) without requiring speculative compliation. That gives a lot more motivation, that was in the paper, I belive it should be treated as totally new feature.


Ville Voutilainen

unread,
Oct 16, 2016, 3:57:48 PM10/16/16
to ISO C++ Standard - Future Proposals
That would be http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf.

The problem with an untyped argument is that it's ambiguous - does it
mean an unnamed parameter of a certain
type, or a parameter of a deduced type? The former is already used by
existing code, so this is a breaking change.

The problem with a single expression that is not inside braces is that
use cases where the lambda would be immediately
called instead of passed as an argument are unattainable, whereas such
problems don't arise with traditional lambdas.
There are also readability concerns with lambdas where the body is not braced.

The issues with terse lambda bodies can perhaps be overcome. The
requirement to have a type for a parameter can't, that
horse is out of the barn. I doubt a completely new lambda syntax that
makes the polar opposite decision for that issue can
survive, it will be murdered at the altar of consistency.

Barry Revzin

unread,
Oct 16, 2016, 4:15:50 PM10/16/16
to ISO C++ Standard - Future Proposals


The problem with a single expression that is not inside braces is that
use cases where the lambda would be immediately
called instead of passed as an argument are unattainable, whereas such
problems don't arise with traditional lambdas.
There are also readability concerns with lambdas where the body is not braced.


But those cases have no need for SFINAE-friendliness or noexcept-correctness since they're never passed to anything. Also most of the time I'm created a lambda to be immediately called is for conditionally initializing something - which wouldn't be a single expression anyway. And if it were a single expression, why would I need a lambda anyway? 

Ville Voutilainen

unread,
Oct 16, 2016, 4:26:33 PM10/16/16
to ISO C++ Standard - Future Proposals
Well, if you can convince EWG that single-expression cases are that
important, and there's
never a need to put another statement into that lambda, you may have a
case. The paper should
perhaps explain why noexcept-correctness is of such importance, and it
should explain why
SFINAE-friendliness is of such importance, and to whom, and writing what.

Nicol Bolas

unread,
Oct 16, 2016, 5:42:20 PM10/16/16
to ISO C++ Standard - Future Proposals


On Sunday, October 16, 2016 at 3:57:48 PM UTC-4, Ville Voutilainen wrote:
On 16 October 2016 at 22:48, Nicol Bolas <jmck...@gmail.com> wrote:
>
>
> On Sunday, October 16, 2016 at 3:43:03 PM UTC-4, Ville Voutilainen wrote:
>>
>> On 16 October 2016 at 22:28, Barry Revzin <barry....@gmail.com> wrote:
>> >>>
>> >>
>> >> Yes, he considered that. It's right there in the document he posted ;)
>> >> Search for "Abbreviated Function Syntax".
>> >
>> >
>> > Yep! Although here's a rewritten version where I'm clarifying that there
>> > are
>> > three independent proposals here: => expr, unary>>, and omitting types
>> > in
>> > lambdas. Hopefully this'll be clearer that the intent of => is to be
>> > used in
>> > both lambdas and functions.
>>
>>
>> Except for unary>>, these features were proposed and rejected in
>> Portland. I don't see much in this
>> proposal that would make the objections made at that time moot.
>
>
> Could you provide some information on what those proposals were and what the
> objections were?


That would be http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf.

The problem with an untyped argument is that it's ambiguous - does it
mean an unnamed parameter of a certain
type, or a parameter of a deduced type? The former is already used by
existing code, so this is a breaking change.

So what about having untyped arguments, so long as it isn't ambiguous? That is, `&&name`, `const &name`, `*name`, etc.
 
The problem with a single expression that is not inside braces is that
use cases where the lambda would be immediately
called instead of passed as an argument are unattainable, whereas such
problems don't arise with traditional lambdas.
There are also readability concerns with lambdas where the body is not braced.

The use of => syntax should help with readability (though personally, I wouldn't be averse to requiring {}). And it is possible to call such a lambda; you just need partheses around the lambda itself: `([]() => expr)()`.
 

Ville Voutilainen

unread,
Oct 16, 2016, 6:11:40 PM10/16/16
to ISO C++ Standard - Future Proposals
On 17 October 2016 at 00:42, Nicol Bolas <jmck...@gmail.com> wrote:
> So what about having untyped arguments, so long as it isn't ambiguous? That
> is, `&&name`, `const &name`, `*name`, etc.

And how do you unambiguously have an unnamed by-value parameter?

> The use of => syntax should help with readability (though personally, I
> wouldn't be averse to requiring {}). And it is possible to call such a
> lambda; you just need partheses around the lambda itself: `([]() =>
> expr)()`.

Works for me.

Nicol Bolas

unread,
Oct 16, 2016, 7:32:52 PM10/16/16
to ISO C++ Standard - Future Proposals
On Sunday, October 16, 2016 at 6:11:40 PM UTC-4, Ville Voutilainen wrote:
On 17 October 2016 at 00:42, Nicol Bolas <jmck...@gmail.com> wrote:
> So what about having untyped arguments, so long as it isn't ambiguous? That
> is, `&&name`, `const &name`, `*name`, etc.

And how do you unambiguously have an unnamed by-value parameter?

You don't (or more accurately, you use `auto`, just like you used to). While I appreciate the importance of by-value parameters, for lambdas, the vast majority of cases will want to take parameters by some form of reference. So if the syntax is sub-optimal for a more rare case, I think we can live with it.

Ville Voutilainen

unread,
Oct 16, 2016, 9:10:28 PM10/16/16
to ISO C++ Standard - Future Proposals
On 17 October 2016 at 02:32, Nicol Bolas <jmck...@gmail.com> wrote:
>> And how do you unambiguously have an unnamed by-value parameter?
>
>
> You don't (or more accurately, you use `auto`, just like you used to). While
> I appreciate the importance of by-value parameters, for lambdas, the vast
> majority of cases will want to take parameters by some form of reference. So
> if the syntax is sub-optimal for a more rare case, I think we can live with
> it.


Yet another rule in addition to usual parameter rules and capture
rules, something between them.
That'll get all 10 seconds of time in Evolution before the group
tosses the idea out.

szollos...@gmail.com

unread,
Oct 17, 2016, 5:28:43 PM10/17/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com

Except that's not what ?: does. If your back-tick things create lambdas, then that's very different from how ?: works. And if your back-tick things don't create lambdas, then it's not clear what's going on.
Can you please help me understand where I go wrong, I fail to see it..
Back-ticks should create lambdas and forward everything. I do intend to redefine ? and : as separate operators, but the end result is (planned to be) the same. ? should call one of the lambdas created.

Thanks,
-lorro

Matthew Woehlke

unread,
Oct 26, 2016, 9:39:43 AM10/26/16
to std-pr...@isocpp.org
On 2016-10-13 12:16, Nicol Bolas wrote:
> 2: Specifying return types and noexcept explicitly in order to properly
> return an expression, with SFINAE intact.
>
> Solving #2 is essentially about having a function whose entire body is
> "return <expr>;". The `=> expr` syntax discussed in this thread seems like
> a good fit here, especially since it could also be applied to regular
> function declarations.

Hmm...

class Foo
{
int m_bar;
public:
auto bar() const => m_bar;
};

WFM.

--
Matthew

Matthew Woehlke

unread,
Oct 31, 2016, 9:34:17 AM10/31/16
to std-pr...@isocpp.org
On 2016-10-16 15:57, Ville Voutilainen wrote:
> There are also readability concerns with lambdas where the body is
> not braced.

My impression (albeit granting that I did not read Barry's paper; this
is just going by the discussion) was that `=>` would be used when the
function/lambda body consists of a single expression with implied
return. I don't see this as being a serious readability issue.

As also noted by Tomaz, N3418 was proposing a different syntax, where
the syntax selected likely was largely responsible for the defeat of
that part of the proposal. Barry's proposal also has broader scope and
is thus useful in more contexts.

(Incidentally, I don't think `=>` should allow a braced expression; that
would give an entirely new and strange meaning to braces. *Parentheses*
on the other hand would make sense. Actually, I think `=>{expr}` should
be equivalent to `{ return {expr}; }`, which of course only makes sense
if the return type is specified.)

Personally, I'd be in favor of a proposal for `>>expr` and `func=>expr`.

--
Matthew

Victor Dyachenko

unread,
Nov 1, 2016, 3:36:18 AM11/1/16
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
On Monday, October 31, 2016 at 4:34:17 PM UTC+3, Matthew Woehlke wrote:
(Incidentally, I don't think `=>` should allow a braced expression; that
would give an entirely new and strange meaning to braces. *Parentheses*
on the other hand would make sense. Actually, I think `=>{expr}` should
be equivalent to `{ return {expr}; }`, which of course only makes sense
if the return type is specified.)
+1. Braces don't add readability here. C# and Java are OK with => expr (-> expr in Java)

Barry Revzin

unread,
Feb 1, 2017, 6:51:57 PM2/1/17
to ISO C++ Standard - Future Proposals
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>). 

If this seems reasonable and agreeable, I'd like to add it to the next mailing.
abbrev_lambda.html

Ricardo Fabiano de Andrade

unread,
Feb 1, 2017, 9:44:39 PM2/1/17
to std-pr...@isocpp.org
Nice idea, the forward operator is also a nice touch.
Maybe out of the scope, but I'm going to ask anyways: have you thought about an equivalent move operator? "operator<<" maybe? :)

On Wed, Feb 1, 2017 at 5:51 PM, Barry Revzin <barry....@gmail.com> wrote:
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>). 

If this seems reasonable and agreeable, I'd like to add it to the next mailing.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Péter Radics

unread,
Feb 2, 2017, 4:05:44 AM2/2/17
to ISO C++ Standard - Future Proposals
Looks nice, although for me a the [](x, y) => x < y syntax looks a bit "naked", I'd rather have something surrounding the expression, so I'd prefer to write [](x, y) => { x < y }.  Then this could also be generalized to multi-statement lambdas, just with the new => syntax to help with your other idea of omitting auto&&.  Would that be viable?  The {} could be optional for single-expression body.

Also, I think you made a typo in the examples section:

[&obj](...args) => obj.func(>>args...)


should probably be

[&obj](args...) => obj.func(>>args...)

(at least according to the previous section of the proposal).

Jared Grubb

unread,
Feb 2, 2017, 12:24:47 PM2/2/17
to ISO C++ Standard - Future Proposals


On Wednesday, February 1, 2017 at 3:51:57 PM UTC-8, Barry Revzin wrote:
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>). 

If this seems reasonable and agreeable, I'd like to add it to the next mailing.

I really like this a lot. Well argued and well written. Good luck!

Nicol Bolas

unread,
Feb 2, 2017, 5:22:31 PM2/2/17
to ISO C++ Standard - Future Proposals
On Wednesday, February 1, 2017 at 6:51:57 PM UTC-5, Barry Revzin wrote:
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>). 

If this seems reasonable and agreeable, I'd like to add it to the next mailing.

Hmm. I really don't think the part about omitting the types is going to fly, parsing wise. Basing it on the use of `=>` syntax requires look-ahead, and that's an uphill battle. As a possible alternative, consider this:

[]:(x, ...y) {};

One advantage of this is that, like `=>` syntax, you could also theoretically apply it to regular functions:

void func_name:(x, ...y) {}; //I'm a template!

The big downside is that with `:()` syntax, you now have to come up with new parsing rules for function parameters. Also, :( looks like an emoji.

Lorand Szollosi

unread,
Feb 3, 2017, 7:35:40 AM2/3/17
to std-pr...@isocpp.org
Hi,

Like it, but miss some parens on the rhs of '=>', esp. because of precedence.

Just an idea: gcc & clang & to some extend, intel cc already have ({ expr }} which 'has the value of' (definition on this differs per compiler) expr. If you said, [&](x, y) => ({ x < y }) as the new syntax, you could:
- have a distinct paren type,
- make it look familiar to those used to statement expressions,
- solve most precedence issues,
- might optionally allow for ({ stmt; expr }) for some statement (perhaps step-by-step)
- might optionally allow for ({ return expr; }), which returns to the caller of the context defining the lambda (think of e.g. accumulate() over range of ints via mul: you want to return early if you see a 0 in the input); UB if context is left,
- might optionally allow for break, continue or even throw, <cough>goto/switch in the defining context.

Thus you could capture continuation replacing statements. All the rest I'd keep the same in the proposal, I really like the idea of simplified lambdas.

Thanks,
-lorro

2017. febr. 2. dátummal, 0:51 időpontban Barry Revzin <barry....@gmail.com> írta:

I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>). 

If this seems reasonable and agreeable, I'd like to add it to the next mailing.

--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/uu7mRNXnf8Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
<abbrev_lambda.html>

Viacheslav Usov

unread,
Feb 3, 2017, 10:09:43 AM2/3/17
to ISO C++ Standard - Future Proposals
I think your idea can be stated more generally as a way to decorate the parameters-and-qualifiers in some way that indicates the abbreviated syntax shall be expected there.

Why can't we use the <> instead of () to that effect? E.g., []<a, b> =>  a + b.

This, in principle, makes the following => symbol redundant, so we can abbreviate that further to []<a, b> a + b.

Cheers,
V.

Nicol Bolas

unread,
Feb 3, 2017, 10:13:42 AM2/3/17
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
On Friday, February 3, 2017 at 7:35:40 AM UTC-5, Lorand Szollosi wrote:
Hi,

Like it, but miss some parens on the rhs of '=>', esp. because of precedence.

Just an idea: gcc & clang & to some extend, intel cc already have ({ expr }} which 'has the value of' (definition on this differs per compiler) expr.

To me, that looks like someone is making a braced-init-list and putting it inside (), which is ill-formed.
 
If you said, [&](x, y) => ({ x < y }) as the new syntax, you could:
- have a distinct paren type,
- make it look familiar to those used to statement expressions,

Why not just standardize "statement expressions" as a separate proposal? They have utility beyond just lambdas.

- solve most precedence issues,

Any precedence issues could be solved just as well with ().

- might optionally allow for ({ stmt; expr }) for some statement (perhaps step-by-step)
- might optionally allow for ({ return expr; }), which returns to the caller of the context defining the lambda (think of e.g. accumulate() over range of ints via mul: you want to return early if you see a 0 in the input); UB if context is left,

No. That's too fragile.

- might optionally allow for break, continue or even throw, <cough>goto/switch in the defining context.

See above. Please don't try to make a different proposal conform to the desires of your own pet feature.

Nicol Bolas

unread,
Feb 3, 2017, 10:16:30 AM2/3/17
to ISO C++ Standard - Future Proposals

Because `<>` means "template parameters/arguments" not function parameters/arguments.

Viacheslav Usov

unread,
Feb 3, 2017, 10:30:24 AM2/3/17
to ISO C++ Standard - Future Proposals
On Fri, Feb 3, 2017 at 4:16 PM, Nicol Bolas <jmck...@gmail.com> wrote:

> Because `<>` means "template parameters/arguments" not function parameters/arguments.

Not after a lambda introducer.

Cheers,
V.

Barry Revzin

unread,
Feb 3, 2017, 10:51:41 AM2/3/17
to ISO C++ Standard - Future Proposals

Hmm. I really don't think the part about omitting the types is going to fly, parsing wise. Basing it on the use of `=>` syntax requires look-ahead, and that's an uphill battle. As a possible alternative, consider this:

[]:(x, ...y) {};


Introducing identifiers with any type annotation is an uphill battle. The lookahead parsing is maybe just a side-skirmish? There's a lot of different directions we could take towards allowing the lack of type annotation:

[](x, y) => x > y     // this proposal
[]@(x, y) => x > y    // : or any other identifier before the parameter list as a marking
                     
// to indicate that there's no type on any of the parameters
[](@x, @y) => x > y   // some marking on the parameters themselves to indicate that there's
                     
// no type (like : or ^ or %, probably not & or *)
[](&&x, &&y) => x > y // this is actually in the original generic lambda proposal: making
                     
// the type-specifier optional.

 All of those seem fine to me and I believe are better than having to write out auto&& everywhere. 

Barry Revzin

unread,
Feb 3, 2017, 10:55:28 AM2/3/17
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Like it, but miss some parens on the rhs of '=>', esp. because of precedence.

You could always write them. I don't see the need for them though.
 

Just an idea: gcc & clang & to some extend, intel cc already have ({ expr }} which 'has the value of' (definition on this differs per compiler) expr. If you said, [&](x, y) => ({ x < y }) as the new syntax, you could:
- have a distinct paren type,
- make it look familiar to those used to statement expressions,
- solve most precedence issues,
- might optionally allow for ({ stmt; expr }) for some statement (perhaps step-by-step)

The problem is that without core language support, the statement-expressions aren't super useful. There's no copy-elision there. And statement-expressions would be a large language change that's worthy of a large proposal.
 
- might optionally allow for ({ return expr; }), which returns to the caller of the context defining the lambda (think of e.g. accumulate() over range of ints via mul: you want to return early if you see a 0 in the input); UB if context is left,
- might optionally allow for break, continue or even throw, <cough>goto/switch in the defining context.

Thus you could capture continuation replacing statements. All the rest I'd keep the same in the proposal, I really like the idea of simplified lambdas.


I still don't understand what any of this means, sorry.  

Nicol Bolas

unread,
Feb 3, 2017, 11:50:17 AM2/3/17
to ISO C++ Standard - Future Proposals

Then your proposal should indicate that you're flexible with how this gets handled. You should list out various alternatives, because I strongly suspect that idea as is will be considered to require too much parsing work to be allowed. It would be good to have a fallback should that happen.

Nicol Bolas

unread,
Feb 3, 2017, 11:53:40 AM2/3/17
to ISO C++ Standard - Future Proposals
I'm not saying it poses a parsing problem. I'm saying it poses a human recognition problem.

We shouldn't use something just because it's a syntax that works. It should be a syntax that will not introduce confusion in users. Historically, <> means template stuff. We should preserve that meaning.

This is also part of why I'm against template induction syntax from Concepts TS: it makes {} mean template stuff too. It does so not because it's a good, recognizable idea, but merely because it's a parse-able and unambiguous syntax.

Viacheslav Usov

unread,
Feb 3, 2017, 12:14:09 PM2/3/17
to ISO C++ Standard - Future Proposals
On Fri, Feb 3, 2017 at 5:53 PM, Nicol Bolas <jmck...@gmail.com> wrote:

> We shouldn't use something just because it's a syntax that works. It should be a syntax that will not introduce confusion in users. Historically, <> means template stuff. We should preserve that meaning.

Frankly, [] historically meant arrays, so the "historically means" argument is pretty weak. Besides, <> means template stuff only when it follows some name, and the stuff within <> would be made of pairs in that case, which (to me) is very different from []<a, b>.

Then, those generic typeless arguments are related to templates. So there is a connection. We can even call abbreviated lambdas "template expressions" to make that connection stronger :)

Finally, the whole point of this thread is abbreviation. Decorating parameters-and-qualifiers with additional symbols is kind of backward here.

Using a different kind of lambda introducer might be a better way, but I seem to remember that was discussed with an earlier version of the proposal.

Cheers,
V.

Barry Revzin

unread,
Feb 3, 2017, 12:22:19 PM2/3/17
to ISO C++ Standard - Future Proposals
There's also http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0428r0.pdf, which I think was positively received in Issaquah. 

via....@gmail.com

unread,
Feb 3, 2017, 1:24:45 PM2/3/17
to Barry Revzin, ISO C++ Standard - Future Proposals

 

Or we can just do away with the entire parenthetical block and specify abbreviated parameters in the square bracket block after some delimiter. For example:

 

[a, b : c, d] a + b + c + d

 

Here, a and b are captures, and c and d are arguments.

 

Cheers,

V.

Avi Kivity

unread,
Feb 3, 2017, 2:34:39 PM2/3/17
to std-pr...@isocpp.org
A suggestion: add move-captures


[&&x] ()

is equivalent to

[x = std::move(x)] () mutable

and perhaps also [&&] to set the capture default.


This is used extensively in asynchronous coding.

Lorand Szollosi

unread,
Feb 3, 2017, 4:22:14 PM2/3/17
to std-pr...@isocpp.org
Hi,

Based on the below, why not just let '@' equals 'auto&&' everywhere (and maybe introduce a digraph if really needed)?

Thanks,
-lorro
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/uu7mRNXnf8Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Vittorio Romeo

unread,
Mar 25, 2017, 9:43:45 AM3/25/17
to ISO C++ Standard - Future Proposals
Are there any news regarding this proposal? (https://wg21.link/p0573)

I'm really really looking forward to this, as it would not only improve code readability immensely, but also open up opportunities for terse and intuitive syntax.
However, one thing that I think would be very important to add to the proposal is the ability to use compound statements after =>.

As far as I can see, the proposed new syntax only allows:
[captures...](args...) => expr
// would expand to
[captures...](args...) -> decltype(expr) { return expr; }

I propose an extension of that, which would support arbitrary compound statements:
[captures...](args...) => { body; }
// would expand to
[captures...](args...) -> decltype(body, void()) { body; }

In case of multiple statements, multiple `decltype` clauses would be generated:
[captures...](args...) => { s0; s1; s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2, void()) { s0; s1; s2; }

In case of a final return, the generated trailing return type would not have the final `void()`:
[captures...](args...) => { s0; s1; return s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2) { s0; s1; return s2; }


As an example, having SFINAE-friendly lambdas with blocks could allow recursive variant visitation to be implemented as follows:
// With a example `json` variant type whose alternatives are:
// * double
// * string
// * vector<json>
// * map<string, json>

json j
= get_json_response();
match(j)([](double x)                          => { /* ... */ },
         
[](string x)                          => { /* ... */ },
         [](auto recurse, vector<json> x)      => { /* ... */ for(auto& v : x)     recurse(v); },
         [](auto recurse, map<string, json> x) => { /* ... */ for(auto& [_,v] : x) recurse(v); });

This is problematic to achieve without this proposal as trying to deduce the arity of a non-SFINAE-friendly lambda can result in an hard error.
(The arity of the visitation branches are used to differentiate between the base and recursive cases.)

If the block is complicated (e.g. it contains multiple return statements constrained by `if constexpr(...)`) then the trailing return generation would get trickier.
I think that P0315 (wg21.link/p0315) "Lambdas in unevaluated contexts" could help here:

[captures...](args...) => { complex_body; }
// would expand to (with P0315)
[captures...](args...) -> decltype([captures...](Ts... xs){ complex_body; }(std::declval<Ts>(xs)...)) { complex_body; }

Nicol Bolas

unread,
Mar 25, 2017, 10:08:00 AM3/25/17
to ISO C++ Standard - Future Proposals
On Saturday, March 25, 2017 at 9:43:45 AM UTC-4, Vittorio Romeo wrote:
Are there any news regarding this proposal? (https://wg21.link/p0573)

I'm really really looking forward to this, as it would not only improve code readability immensely, but also open up opportunities for terse and intuitive syntax.
However, one thing that I think would be very important to add to the proposal is the ability to use compound statements after =>.

As far as I can see, the proposed new syntax only allows:
[captures...](args...) => expr
// would expand to
[captures...](args...) -> decltype(expr) { return expr; }

I propose an extension of that, which would support arbitrary compound statements:
[captures...](args...) => { body; }
// would expand to
[captures...](args...) -> decltype(body, void()) { body; }

In case of multiple statements, multiple `decltype` clauses would be generated:

That makes no sense, as you cannot apply `decltype` to statements; it can only be applied to expressions. That's why `=>` syntax requires a single expression.
 
[captures...](args...) => { s0; s1; s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2, void()) { s0; s1; s2; }

In case of a final return, the generated trailing return type would not have the final `void()`:
[captures...](args...) => { s0; s1; return s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2) { s0; s1; return s2; }


As an example, having SFINAE-friendly lambdas with blocks could allow recursive variant visitation to be implemented as follows:

Or you could just have a recursive visitation function. Or do what Boost does for recursive visitation. Or something else.

The general problem of applying SFINAE to the entire contents of a function definition is a very different one from the one being solved here.

Thiago Macieira

unread,
Mar 25, 2017, 11:34:10 AM3/25/17
to std-pr...@isocpp.org
On sábado, 25 de março de 2017 06:43:45 PDT Vittorio Romeo wrote:
> I propose an extension of that, which would support arbitrary compound
> statements:
> [captures...](args...) => { body; }
> // would expand to
> [captures...](args...) -> decltype(body, void()) { body; }

How is this different from

[captures....](args) { body; }

?

What's the advantage provided by those three extra keystrokes?

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Vittorio Romeo

unread,
Mar 25, 2017, 11:43:34 AM3/25/17
to ISO C++ Standard - Future Proposals
auto l = [](auto x){ x.foo(); };
static_assert(!std::is_invocable<decltype(l), int>::value);

The code above will produce an hard error:
prog.cc:3:24: error: request for member 'foo' in 'x', which is of non-class type 'int'
 auto l = [](auto x){ x.foo(); };
                      ~~^~~

---

auto l = [](auto x) -> decltype(x.foo(), void()) { x.foo(); };
static_assert(!std::is_invocable<decltype(l), int>::value);

The code above will work as intended.
On wandbox: https://wandbox.org/permlink/oxExZ7WPNGS0YV2e

---

More info: 
http://stackoverflow.com/questions/42739837/getting-sfinae-to-work-with-is-callable-on-an-overloaded-function-object

Vittorio Romeo

unread,
Mar 25, 2017, 11:52:29 AM3/25/17
to ISO C++ Standard - Future Proposals
Yeah, it doesn't make sense - I posted too quickly without thinking enough about it.
Even my last example with the unevaluated context lambda wouldn't work, as that lambda itself would need a proper `-> decltype(...)` and `noexcept(noexcept(...))`.
I'm starting to realize that computing the result of `decltype(block)` and `noexcept(noexcept(block))` is a very difficult task...

Thiago Macieira

unread,
Mar 25, 2017, 11:55:18 AM3/25/17
to std-pr...@isocpp.org, Vittorio Romeo
On sábado, 25 de março de 2017 08:43:34 PDT Vittorio Romeo wrote:
> auto l = [](auto x){ x.foo(); };
> static_assert(!std::is_invocable<decltype(l), int>::value);
>
> The code above will produce an hard error:
>
> prog.cc:3:24: error: request for member 'foo' in 'x', which is of non-class
> type 'int' auto l = [](auto x){ x.foo(); };
> ~~^~~

You didn't answer my question.

And this compiles:

struct S { void foo(); };
auto l = [](S x){ x.foo(); };
static_assert(!std::is_invocable<decltype(l), int>::value);

https://wandbox.org/permlink/VcoZff9PjRCHCFbx

Vittorio Romeo

unread,
Mar 25, 2017, 12:14:29 PM3/25/17
to ISO C++ Standard - Future Proposals, vittorio....@gmail.com
Were you referring to the `...` in `args...`? 
If that's the case, it was just pseudocode that means "anything can be in here" - I was trying to show expansion examples for lambdas with arbitrary captures and arbitrary signatures.

And this compiles

It does, but the lambda is not generic anymore. You example compiles because the error does not occur during instantiation, it occurs during overload resolution.
In my example, the
l(0)
call expression is valid - it's the instantiation of `l<int>` that is invalid.

In your example, the
l(0)
call expression is itself invalid - that can be detected by `is_invocable` without causing an hard error.

 

Thiago Macieira

unread,
Mar 25, 2017, 5:16:40 PM3/25/17
to std-pr...@isocpp.org
On sábado, 25 de março de 2017 09:14:28 PDT Vittorio Romeo wrote:
> Were you referring to the `...` in `args...`?

No. I thought it was a placeholder.

> It does, but the lambda is not generic anymore. You example compiles
> because the error does not occur during instantiation, it occurs during
> overload resolution.

Right, but why does the lambda have to be generic?

> In my example, the
> l(0)
> call expression is valid - it's the instantiation of `l<int>` that is
> invalid.
>
> In your example, the
> l(0)
> call expression is itself invalid - that can be detected by `is_invocable`
> without causing an hard error.

I know.

Please be clear: what's the advantage of using
=> { statements; }
over
{ statements; }
for the same lambda? What can the suggested syntax do that the existing syntax
can't?

Nicol Bolas

unread,
Mar 25, 2017, 5:42:27 PM3/25/17
to ISO C++ Standard - Future Proposals


On Saturday, March 25, 2017 at 5:16:40 PM UTC-4, Thiago Macieira wrote:
On sábado, 25 de março de 2017 09:14:28 PDT Vittorio Romeo wrote:
> Were you referring to the `...` in `args...`?

No. I thought it was a placeholder.

> It does, but the lambda is not generic anymore. You example compiles
> because the error does not occur during instantiation, it occurs during
> overload resolution.

Right, but why does the lambda have to be generic?

Because that's the point: the functor is callable on any type which satisfies the criteria. If a type doesn't satisfy the criteria, then `is_callable` for those parameters will be false, rather than provoking a compile error.

He essentially wants a general solution to the SFINAE problem: for all of a function's expressions to provoke SFINAE if they are il-formed.

> In my example, the
> l(0)
> call expression is valid - it's the instantiation of `l<int>` that is
> invalid.
>
> In your example, the
> l(0)
> call expression is itself invalid - that can be detected by `is_invocable`
> without causing an hard error.

I know.

Please be clear: what's the advantage of using
        => { statements; }
over
        { statements; }
for the same lambda? What can the suggested syntax do that the existing syntax
can't?

He just told you what the advantage was: you can ask the question of whether the lambda is callable with a particular parameter without causing a compile error. This is why people still use `decltype(expression)` instead of `decltype(auto)` with many return type declarations: because the former provokes SFINAE and the latter does not.
It is loading more messages.
0 new messages