P2266’s interaction with 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:

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&
Posted 2021-05-04