36

Today I found this code

#include <cstdio>

auto terminal = [](auto term)           
{                                       
    return [=] (auto func)             
    {                                   
        return terminal(func(term));
    };
};

Surprisingly, GCC accepts it. Clang rejects it because it uses terminal in its own intializer and is declared auto.

I was expecting the error that clang gave, but is it actually ill-formed? Or must the code be accepted?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    This appears to be making use of polymorphic lambdas which are a feature in C++14. Check to see if your gcc version and / or your Clang version have support for polymorphic lambdas. – YoungJohn Sep 05 '14 at 20:46
  • @YoungJohn, The GCC version clearly does if it compiles the code. I can say with confidence that the Clang version on Coliru both supports polymorphic lambdas and does not compile the code. – chris Sep 05 '14 at 20:46
  • @YoungJohn thanks for your advices. I checked GCC and it has support for polymorphic lambdas (as it accepts and executes my snippet). Clang [seems to support them too](http://coliru.stacked-crooked.com/a/e89ece0828ca7484). – Johannes Schaub - litb Sep 05 '14 at 20:47
  • So GCC only seems to complain when I change the function to actually call *both* of those lambdas in the declaration statement and return statement (and comment out the call in `main` because it's no longer compatible at that point). – chris Sep 05 '14 at 20:48
  • @chris can you please show an example? – Johannes Schaub - litb Sep 05 '14 at 20:52
  • @JohannesSchaub-litb, [Here you go.](http://coliru.stacked-crooked.com/a/1715a37b1b624774) Unless I'm really out of it, removing either call will make it compile. – chris Sep 05 '14 at 20:54
  • @chris I'm not sure if I'm surprised: once you call the function, you need the return type obviously. In your examples, the return type is dependent if you remove either call, so the return type is not determined after the initialization of `terminal`. I.e. if you only have the outer call, you'll initialize `terminal` with a closure object. The type of this object does not depend on the outer lambda AFAIK. If you only have the inner call, this call is not performed until calling the outer lambda. – dyp Sep 05 '14 at 20:57
  • Interestingly, g++ rejects it when the outer lambda is not polymorphic: http://coliru.stacked-crooked.com/a/e9e3ce4f1b2d79da – dyp Sep 05 '14 at 21:07
  • I started to post something but then I realized I don't know enough of lambda types... terminal gets initialized with a lambda that returns another lambda, shouldn't that be enough to determine its type? – Marco A. Sep 05 '14 at 21:08
  • For anyone interested, here is the std-discussion mirror of the question: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/PWSmffzcwQ0 – Johannes Schaub - litb Sep 05 '14 at 21:10
  • 1
    related not really http://stackoverflow.com/q/25618934/560648 – Lightness Races in Orbit Sep 05 '14 at 22:01
  • Looks like we need a C++14 tag on this question as well as C++11 – CashCow Sep 08 '14 at 08:25

1 Answers1

15

I think this runs into §7.1.6.4 [dcl.spec.auto]/p11:

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.

You need the type of terminal to determine the type of the id-expression terminal in return terminal(func(term)); (edited, hat tip @Richard Smith), but at the point of that expression you can't deduce the type of terminal yet.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • 5
    The return type of the apply-operator is dependent, it must be determined only when instantiating. – dyp Sep 05 '14 at 20:51
  • 4
    You need the type of `terminal` to determine the type of the *id-expression* `terminal`, so the program is ill-formed. – Richard Smith Sep 05 '14 at 21:47
  • 1
    Thanks. To summarize: The spec currently is not clear whether `terminal`'s reference to the auto variable must be rejected at the time of definition of the member template `operator()` or accepted at its instantiation (at which time the variable no longer is `auto`). The situation is handled by http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1850 and makes this case ill-formed, according to @RichardSmith's reply on std-discussion – Johannes Schaub - litb Sep 05 '14 at 22:38