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& |