P2266’s interaction with decltype(auto)
decltype(auto)C++Now 2021 is happening this week. Normally it’s in Aspen, Colorado, but this year it’s completely online. I presented two talks:
-
“When Should You Give Two Things the Same Name?,” based on this blog post of mine from October 2020
-
“The Complete Guide to
return x,” based on my committee papers P1155 “More Implicit Move” (adopted into C++20) and P2266 “Simpler Implicit Move” (proposed for C++23).
In the latter talk, my slide 77 showed a table illustrating how P2266’s value-category changes affected five subtly different functions. One of the entries was incorrect; and, as I fixed it, I realized that for completeness the table should have included eight subtly different functions!
Here is a revised and I hope fully correct version of that table. This revised version appears in the slide deck I submitted after the talk.
| C++14/17/20 | P2266 (C++23?) | |
|---|---|---|
auto a(T x) -> decltype(x) { return x; } |
T 1 |
T 1 |
auto b(T x) -> decltype((x)) { return (x); } |
T& |
T& |
auto c(T x) -> decltype(auto) { return x; } |
T 1 |
T 1 |
auto d(T x) -> decltype(auto) { return (x); } |
T& |
T&& |
auto e(T&& x) -> decltype(x) { return x; } |
ill-formed 2 | T&& |
auto f(T&& x) -> decltype((x)) { return (x); } |
T& |
ill-formed 3 |
auto g(T&& x) -> decltype(auto) { return x; } |
ill-formed 2 | T&& |
auto h(T&& x) -> decltype(auto) { return (x); } |
T& |
T&& 4 |
1) Implicit move: the returned T object is move-constructed from x.
2) The return type is deduced as T&&, but lvalue x cannot be returned as T&& without a cast.
(C++20’s change to permit implicit-moving from rvalue reference variables is irrelevant.
Implicit move is not considered, because this is not a copy-initialization context.)
3) The return type is T&; but xvalue x cannot be returned as T& without a cast.
4) In C++20, the return type is deduced as T& because the returned expression (x) is an lvalue.
After P2266, the return type is deduced as T&& because the returned expression (x) is an xvalue.
Observe that in C++20, functions f and h both do “rvalue laundering,”
accepting an rvalue reference and returning an lvalue reference, with no
visible cast. After P2266,
neither of them works anymore to launder rvalues: f becomes ill-formed,
and h becomes the identity function.
And just for total completeness:
| C++14/17/20 | P2266 (C++23?) | |
|---|---|---|
auto t(T& x) -> decltype(x) { return x; } |
T& |
T& |
auto u(T& x) -> decltype((x)) { return (x); } |
T& |
T& |
auto v(T& x) -> decltype(auto) { return x; } |
T& |
T& |
auto w(T& x) -> decltype(auto) { return (x); } |
T& |
T& |
