[RFC] Allow C++11 lambdas in C++03 as an extension

I’m proposing to extend C++03 to accept C++11 lambdas. This would make it easier to write code that has to work in all language modes, but is only required to work with Clang in C++03 (like libc++ does). This should be a conforming extension, since there is no similar structure in C++03 that we could parse differently with the extension that I’m aware of.

There are a few things which need consideration regarding the interaction with other C++ features:

  • noexcept, constexpr, requires, consteval: Since they are keywords that are only available in C++11 (or C++20), I don’t think we want to make them available in some special way. We could add aliases in the reserved namespace if we want to make them available, but that would be a different effort. If that happens we should of course make sure that they work with lambdas.
  • trailing return types: I think we want them to work for lambdas, since there is no other way to specify a return type. For normal functions we could also enable this, but that seems like it should also be a different effort.
  • C++11 attributes: They should work as expected, since they are available in C++03 as an extension already.
  • static operator(), generalized lambda captures, generic lambdas, familiar template syntax for generic lambdas: These should work, since they’ve been back-ported to C++11 already. There is no reason I’m aware of to not allow them in C++03.

[clang] Accept lambdas in C++03 as an extensions by philnik777 · Pull Request #73376 · llvm/llvm-project · GitHub implements this proposal correctly to the best of my knowledge, but I may not have tests for everything yet.

CC @AaronBallman @cor3ntin

1 Like

Thank you for the RFC! I think the idea is reasonable in principle, but the devil will be in the details.

Use of auto is an interesting one as well; we already support some uses of auto in C++03 mode, but we don’t seem to support its use in function parameters or return types: Compiler Explorer – so I think we should probably not allow generic lambdas either.

What about init captures and newer lambda features that don’t require new keywords?

Given how large of a feature it is and how many other features it interacts with, it may help us to reason about the extension if there was a table of lambda features listing out which ones you envision supporting and which ones you don’t.

Thanks for saying this… I’ve been trying for an hour to come up with a way to make myself comfortable with allowing lambdas in pre-c++11 mode. I don’t think trailing-return-type without allowing it on functions as well is a level of consistency I’d like to enable either. But yes, an analysis of all of the ‘surrounding features’ and why we want/don’t want them would be great. I’m leaning towards “please no”, if only because of the size of that feature list.

Also, do we know if there is any similar efforts/prior art in the other compilers that might help guide us?

Thanks for the feedback! I’ve checked GCC, ICC and NVC++. All of them support lambdas in C++03. MSVC doesn’t have C++03 support. I’ll be referring to them in the ‘Prior Art’ column. Please tell me if I missed any features.

Feature Allowed? Reasoning Prior Art
noexcept, constexpr, requires, consteval No These aren’t keywords in C++03 and providing these in some magic way would be a lot of work with somewhat questionable benefit, since nothing else can use them. Aliases for these keywords could be added in the reserved namespace, but that isn’t part of this RFC. None of the compilers seem to support this
trailing return types Yes There is no other way to specify the return type of a lambda and lambdas are an extension already, so there is no ambiguity problem. Given the prior art I think it’s fine to back-port this only for lambdas for now. We can consider allowing it on normal functions later if we still want to. All compilers support it on lambdas, but none on normal functions.
C++11 attributes Yes We already support C++11 attributes in C++03. Using both extensions together should work the same as in C++11. All compilers support both C++11 attributes and C++11 attributes on lambdas.
static operator() Yes static is already accepted on operator() member functions in classes and not allowing them on lambdas would just be inconsistent. This is already back-ported to C++11. GCC accepts static on lambdas. ICC and NVC++ don’t support it. It looks like they also don’t support it in C++23 yet.
generalized lambda captures Yes This is already supported in C++11 and doesn’t rely on any other language feature. All compilers support this.
generic lambdas No As Aaron pointed this would be quite inconsistent between lambdas and normal function; basically requiring different argument parsing depending on which one is currently being parsed. Given prior art, this doesn’t seem like a good idea to back-port. None of the compilers support this.
familiar template syntax Yes This syntax is equivalent to the traditional template function syntax. I don’t think there is any ambiguity what the arguments should be. This has been back-ported to C++11 already. GCC supports this. ICC and NVC++ don’t in C++03, but they do in C++20.
optional () (basically P1102R2: Down with ()!) Yes I don’t see any reason we shouldn’t back-port this. The syntax is unambiguous AFAICT. It’s already back-ported to C++11. GCC accepts it. ICC and NVC++ reject it in C++03. ICC also rejects it in C++23. NVC++ accepts it in C++23.
2 Likes

Thank you, this table is super helpful! I don’t see any features you’ve missed in the list.

re noexcept: we should make sure that []() throw() {} and alternatives behave as expected. It probably already works given that we seem to support that in C++11 mode: Compiler Explorer

re trailing return types: I think it’s reasonable to support on lambdas but not on free or member functions. It’s a bit strange, but that’s the consistent behavior of other compilers so we may as well go with it.

That said, I think the list of what you expect to support and what you expect to not support seems reasonable to me.

1 Like

The generic lambda case was the one that I found most concerning and looking at your table of which features you plan on supporting it seems reasonable. I am mostly comfortable with this.