Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Rationale for lambda-expressions not being allowed in unevaluated contexts

226 views
Skip to first unread message

Matt Calabrese

unread,
Nov 23, 2010, 2:18:24 AM11/23/10
to

In the current working paper at 5.1.2 p2 I notice that it is specified
that "a lambda-expression shall not appear in an unevaluated operand".
I was wondering what the rationale is for this? I ask because I have a
use-case:

////////////////////
///// Implementation
#define AUTO_FUNCTION( ... )\
auto __VA_ARGS__ -> AUTO_FUNCTION_IMPL

#define AUTO_FUNCTION_IMPL( ... )\
decltype( [&]{ __VA_ARGS__ }() ) { __VA_ARGS__ }

///// Usage
template< class L, class R >
AUTO_FUNCTION( add( L left, R right ) )
(
return left + right;
)
////////////////////

This is just one example but it seems to me that there are likely
other use-cases as well. I don't immediately see why there is this
limitation on lambdas, though given that such usage is explicitly
mentioned, I'd imagine that there is a rationale.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Nov 23, 2010, 4:24:56 AM11/23/10
to

On 11/23/2010 08:18, Matt Calabrese wrote:
>
> In the current working paper at 5.1.2 p2 I notice that it is specified
> that "a lambda-expression shall not appear in an unevaluated operand".
> I was wondering what the rationale is for this? I ask because I have a
> use-case:
>
> ////////////////////
> ///// Implementation
> #define AUTO_FUNCTION( ... )\
> auto __VA_ARGS__ -> AUTO_FUNCTION_IMPL
>
> #define AUTO_FUNCTION_IMPL( ... )\
> decltype( [&]{ __VA_ARGS__ }() ) { __VA_ARGS__ }
>
> ///// Usage
> template< class L, class R>
> AUTO_FUNCTION( add( L left, R right ) )
> (
> return left + right;
> )
> ////////////////////
>
> This is just one example but it seems to me that there are likely
> other use-cases as well. I don't immediately see why there is this
> limitation on lambdas, though given that such usage is explicitly
> mentioned, I'd imagine that there is a rationale.

There would indeed exist a huge number of use-cases for allowing lambda
expressions, it would probably extremely extend possible sfinae cases
(to include complete code "sand-boxes"). The reason why they became
excluded was due to exactly this extreme extension of sfinae cases (you
were opening a Pandora box for the compiler) and the fact that it can
lead to problems on other examples as yours, e.g.

template<typename T, typename U>
void g(T, U, decltype([](T x, T y) { return x + y; }) func);

is useless, because every lambda expression generates a unique type, so
something like

g(1, 2, [](int x, int y) { return x + y; });

doesn't actually work, because the type of the lambda used in the
parameter is different from the type of the lambda in the call to g.

Finally it did also cause name-mangling issues. E.g. when you have

template<typename T>
void f(T, A<sizeof([](T x, T y) { return x + y; })> * = 0);

in one translation unit but

template<typename T>
void f(T, A<sizeof([](T x, T y) { return x - y; })> * = 0);

in another translation unit. Assume now that you instantiate f<int>
from both translation units. These two functions have different
signatures, so they must produce differently-mangled template
instantiations. The only way to keep them separate is to mangle the
*body* of the lambdas. That, in turn, means that compiler writers have
to come up with name mangling rules for every kind of statement in the
language. While technically possible, this was considered as both a
specification and an implementation burden.

HTH & Greetings from Bremen,

Daniel Krügler

Daniel Krügler

unread,
Nov 23, 2010, 4:26:07 AM11/23/10
to

On 11/23/2010 08:18, Matt Calabrese wrote:
>
> ////////////////////
> ///// Implementation
> #define AUTO_FUNCTION( ... )\
> auto __VA_ARGS__ -> AUTO_FUNCTION_IMPL
>
> #define AUTO_FUNCTION_IMPL( ... )\
> decltype( [&]{ __VA_ARGS__ }() ) { __VA_ARGS__ }
>
> ///// Usage
> template< class L, class R>
> AUTO_FUNCTION( add( L left, R right ) )
> (
> return left + right;
> )
> ////////////////////
>
> This is just one example but it seems to me that there are likely
> other use-cases as well. I don't immediately see why there is this
> limitation on lambdas, though given that such usage is explicitly
> mentioned, I'd imagine that there is a rationale.

In addition to what I wrote in my first reply, it would IMO make much
more sense in regard to your example to extend the language to allow for
auto return type deduction for function definitions such that

template< class L, class R>

auto add( L left, R right )


(
return left + right;
)

would just become well-formed. The not-so-nice part of this suggestion
is that decltype and return type deduction in lambda-expressions have
slightly different semantics.

HTH & Greetings from Bremen,

Daniel Krügler


SG

unread,
Nov 23, 2010, 4:25:24 AM11/23/10
to

On 23 Nov., 08:18, Matt Calabrese wrote:
> In the current working paper at 5.1.2 p2 I notice that it is specified
> that "a lambda-expression shall not appear in an unevaluated operand".
> I was wondering what the rationale is for this?
>
> I ask because I have a
> use-case:
>
> ////////////////////
> ///// Implementation
> #define AUTO_FUNCTION( ... )\
> auto __VA_ARGS__ -> AUTO_FUNCTION_IMPL
>
> #define AUTO_FUNCTION_IMPL( ... )\
> decltype( [&]{ __VA_ARGS__ }() ) { __VA_ARGS__ }
>
> ///// Usage
> template< class L, class R >
> AUTO_FUNCTION( add( L left, R right ) )
> (
> return left + right;
> )
> ////////////////////

Read on (paragraph 3):

"The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed nonunion class type..."

So, at least the use of decltype makes little sense because two lambda
expression never have the same type.

It seems, what you're trying to do is to remove the redundancy we're
currently stuck with (unfortunately) with respect to the '->'-syntax
for the late return type. But I don't see how this has anything to do
with lambda expressions. Try this:

#define AUTOFUN_IMPL(...) -> decltype(__VAR_ARGS__) \
{return __VAR_ARGS__;}

template<class L, class R>

auto add(L left, R right)
AUTOFUN_IMPL(left+right)

In my opinion, C++0x should treat "auto functions" with a lacking "->
type" the same way it handles lambda expressions already. The function
could be implicitly inline and needs to have a single statement of the
form "return expression;" as its body. However, as far as I know, the
"-> type" part is not optional for non-lambdas. :-(

In other cases you might be able to use 'auto' like this:

auto fun = [](int x, int y){return x>y;}
std::set<int,decltype(fun)> myset (fun);

Here, decltype is not applied on a lambda expression but an object of
a corresponding closure type which is fine.

Cheers,
SG

Matt Calabrese

unread,
Nov 23, 2010, 7:45:41 PM11/23/10
to

On Nov 23, 4:24 am, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:

> There would indeed exist a huge number of use-cases for allowing lambda
> expressions, it would probably extremely extend possible sfinae cases
> (to include complete code "sand-boxes"). The reason why they became
> excluded was due to exactly this extreme extension of sfinae cases

Ha, well, I actually was hoping it would work with SFINAE there too,
but I guess I should have figured that was one of the very reasons why
it's not allowed. Thanks for the very detailed reply.

Matt Calabrese

unread,
Nov 23, 2010, 7:47:11 PM11/23/10
to

On Nov 23, 4:25 am, SG <s.gesem...@gmail.com> wrote:
> It seems, what you're trying to do is to remove the redundancy we're
> currently stuck with (unfortunately) with respect to the '->'-syntax
> for the late return type. But I don't see how this has anything to do
> with lambda expressions. Try this:
>
> #define AUTOFUN_IMPL(...) -> decltype(__VAR_ARGS__) \
> {return __VAR_ARGS__;}
>
> template<class L, class R>
> auto add(L left, R right)
> AUTOFUN_IMPL(left+right)

That is actually much more similar to how I originally implemented the
macro, though I was hoping that a clever use of lambdas could get
exactly what lambdas provide, though as I've learned, it's disallowed.
Anyway, as was said, I think function templates should really be
allowed to have automatically deduced return types much like
lambdas :/ It's sort of an embarrassment that we even have to make a
macro to get rid of this redundancy particularly when another feature
in the language already does pretty much exactly what is wanted.

> In other cases you might be able to use 'auto' like this:
>
> auto fun = [](int x, int y){return x>y;}

Right, but lambdas can't be templates and can't be overloaded, so uses
are very limited when trying to use lambdas simply for an
automatically deduced return type.

Andy Venikov

unread,
Nov 23, 2010, 7:40:40 PM11/23/10
to

On 11/23/2010 4:26 AM, Daniel Krügler wrote:
<snip>

> In addition to what I wrote in my first reply, it would IMO make much
> more sense in regard to your example to extend the language to allow for
> auto return type deduction for function definitions such that
>
> template< class L, class R>
> auto add( L left, R right )
> (
> return left + right;
> )
>
> would just become well-formed.

Hear, hear!
It's just an embarrassment to have to duplicate small pieces of code.
I get a lough out of my lisp friends every time I mention the new "auto deduced" function syntax. It actually supposed to help reduce boilerplate code. Most of the time the body of these functions is a small (single-line) expression (just like in Daniel's example) that should be trivial to deduce the type from. And it's a shame to have to duplicate it verbatim. Moreover, it seems to work just fine for c++0x lambdas, but not for the auto return.

I'm sure that there have been plenty of discussions on this (right?), but could we please have another one? I know it's probably too late for the inclusion into the standard, but generating a healthy discussion is always helpful. I'm sure that there's a growing body of C++ engineers who are deeply dissatisfied with the current state of auto deduced functions.

So, what was the reason to ALWAYS require ->decltype, and is there any chance to change that in the standard?

<snip>


Thanks,
Andy.

Zeljko Vrba

unread,
Nov 24, 2010, 4:49:46 AM11/24/10
to

On 2010-11-23, Daniel Krügler <daniel....@googlemail.com> wrote:
>
> instantiations. The only way to keep them separate is to mangle the
> *body* of the lambdas. That, in turn, means that compiler writers have
> to come up with name mangling rules for every kind of statement in the
> language. While technically possible, this was considered as both a
> specification and an implementation burden.
>
I never understood why mangling is such a huge problem. Define a canonical
representation (why not use the parse tree?), compute a SHA1 hash over it and
use the hash as the mangled name. Include a mapping from hash values to
human-readable text in debug info. Problem solved, or am I missing something?

Matt Calabrese

unread,
Nov 24, 2010, 2:25:07 PM11/24/10
to

On Nov 23, 4:24 am, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:
> There would indeed exist a huge number of use-cases for allowing lambda
> expressions, it would probably extremely extend possible sfinae cases
> (to include complete code "sand-boxes"). The reason why they became
> excluded was due to exactly this extreme extension of sfinae cases

While from what I understand it's likely too late to add automatically
deduced return types to regular functions and function templates to
the standard, would it be feasible to allow lambdas in unevaluated
contexts with it also being specified in the standard that
substitution failure in the body of a lambda function would cause
error rather than trigger SFINAE? This at least allows for some of the
use-cases of lambdas in unevaluated concepts (for instance, the macro
I presented would work, though it would directly produce an error
during substitution if left + right isn't valid for the respective
types). Would this be an acceptable trade-off and simple enough to
squeeze into the standard?

Daniel Krügler

unread,
Nov 25, 2010, 3:40:24 AM11/25/10
to
Am 24.11.2010 20:25, schrieb Matt Calabrese:
>
> On Nov 23, 4:24 am, Daniel Kr�gler<daniel.krueg...@googlemail.com>

> wrote:
>> There would indeed exist a huge number of use-cases for allowing lambda
>> expressions, it would probably extremely extend possible sfinae cases
>> (to include complete code "sand-boxes"). The reason why they became
>> excluded was due to exactly this extreme extension of sfinae cases
>
> While from what I understand it's likely too late to add automatically
> deduced return types to regular functions and function templates to
> the standard, would it be feasible to allow lambdas in unevaluated
> contexts with it also being specified in the standard that
> substitution failure in the body of a lambda function would cause
> error rather than trigger SFINAE? This at least allows for some of the
> use-cases of lambdas in unevaluated concepts (for instance, the macro
> I presented would work, though it would directly produce an error
> during substitution if left + right isn't valid for the respective
> types). Would this be an acceptable trade-off and simple enough to
> squeeze into the standard?

My personal opinion is, that auto return type deduction of function
definitions will get much easier supported than allowing for lambda
expressions in unevaluated contexts (even with the restrictions you
mention). I don't expect that either of them will be added for C++0x,
but the former has IMO a much lesser barrier. AFAIK there do not exist
*any* technical nor fundamental wording problems that speak against the
first alternative, while there speak both technical *and* wording
problems against the second one. I think the former was just not added
because it was considered to have too much feature character when it was
first suggested and/or there was too little time in the end to provide
them. Further, there is a subtle difference compared to decltype
deduction, which requires some thinking about the "right way".
Lambda auto-deduced return types perform lvalue-to-rvalue conversion,
array-to-pointer conversion, and function-to-pointer conversion on the
actual type of the return expression, but decltype does not.

HTH & Greetings from Bremen,

Daniel Kr�gler

0 new messages