In C++20 we can use lambda expressions in unevaluated contexts. Consider the following C++20 code:
foo.cpp
#include <typeinfo>
struct Foo {
using T = decltype([]{});
}; // struct Foo
const std::type_info& getType() { return typeid(typename Foo::T); }
main.cpp
#include <iostream>
#include <typeinfo>
struct Foo {
using T = decltype([]{});
}; // struct Foo
const std::type_info& getType();
const std::type_info& getTypeFromMain() { return typeid(typename Foo::T); }
int main() {
std::cout << std::boolalpha
<< (getType() == getTypeFromMain());
return 0;
}
What should the program output under standard C++?
AFAIK, GCC and clang output false. They believe that all lambda expressions should produce distinct closure types and are "local" to translation units.
On the other hand, [basic.def.odr] 13.10 states that closure types introduced by corresponding lambda expressions in different definitions of the same entity within different translation units should be the same, and the program should output true. Are GCC and clang wrong on this?
Yes, the closure type is the same in all translation units, and the compilers are still catching up to that. The mechanism for doing so is generating (mangled) names for the closure type from its placement within the overall definition (by, say, counting them, as mentioned in the comments). The reason in the standard that this works is somewhat subtle.
[basic.def.odr]/13.10 does not specify that the closure types are the same; it uses "shall" and so requires that they be the same (which, given the following, has an effect only in even more subtle cases). It is /14 that has that effect: "the behavior is as if there is a single entity with a single definition", which necessarily produces a single closure type. Even then, it's not proper to say that the lambda expressions "produce the same closure type": rather, only one of the closure types produced has any relevance to the program as a whole.
Note that without provisions like this, simple functions like
#ifndef MY_HEADER_HH
#define MY_HEADER_HH
inline auto make_assign(int i) {return [i](int &x) {x=i;};}
#endif
wouldn't work if, say, multiple translation units tried to add various make_assign(…) objects to a std::vector<decltype(make_assign(0))>.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With