xmllmx
xmllmx

Reputation: 42255

Why does decltype(auto) not work as expected?

#include <type_traits>
#include <utility>

int main()
{
    auto f1 = [](auto&& e) -> auto
    {
        return e;
    };

    auto f2 = [](auto&& e) -> auto&
    {
        return e;
    };

    auto f3 = [](auto&& e) -> auto&&
    {
        return e;
    };

    auto f4 = [](auto&& e) -> decltype(auto)
    {
        return e;
    };

    int n{};

    f1(std::move(n)); // ok
    f2(std::move(n)); // ok
    f3(std::move(n)); // ok
    f4(std::move(n)); // error
}

clang's error message:

error : rvalue reference to type 'int' cannot bind to lvalue of type 'int'

To me, decltype(auto) has only three possible deduced types:

  1. auto
  2. auto&
  3. auto&&

Why is f4 error while all of the other three are ok?

Upvotes: 2

Views: 640

Answers (1)

Jans
Jans

Reputation: 11250

It's a GCC bug.

decltype(auto) = e is equivalent to decltype(e) and yield the declared type of e.

auto works as template parameter which mean that auto&& is the same as T&& (forwarding reference) for an invented template parameter.

For f1 the return type is deduced to int.

For f2 the return type auto& is equivalent to T& with deduced T=int which is the type of the lvalue e, here you're binding int& to e.

For f3 consider this:

auto&& t = n;
static_assert(std::is_same_v<decltype(t), int&>); // true

for both the return of f3, auto&& is equivalent to the invented template parameter T&& which is a forwarding reference, this initialized with an lvalue, yield T& with deduced T=int, then again ... binding int& to the lvalue e.

Now for f4 consider this:

int&& r = 9;
static_assert(std::is_same_v<decltype(r), int&&>); // true (1)

decltype(auto) t = r; // fail with the same error you got.

the parameter of f4 is also a forwarding reference T&& that is initialized with the xvalue std::move(n), this deduce T=int resulting in a parameter int&& e. A return type as decltype(auto) with return e means that the actual return is decltype(e), then as you can see (1) is true, the same hold for decltype(e), this means that the actual return of f4 is int&& ... and there's the problem, f4 is trying to bind a rvalue int&& to an lvalue e which is prohibited.

You can also take a look at @StoryTeller's answer for the GCC bug.

Upvotes: 4

Related Questions