Bug 64892 - [C++14] generic lambdas, decltype(auto), and rvalue references, oh my!
Summary: [C++14] generic lambdas, decltype(auto), and rvalue references, oh my!
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
Depends on:
Blocks: decltype
  Show dependency treegraph
 
Reported: 2015-02-01 01:10 UTC by Eric Niebler
Modified: 2020-11-05 21:38 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2015-03-05 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Niebler 2015-02-01 01:10:46 UTC
In the following code, gcc seems to be getting the value category wrong for the return type of the generic lambda:

#include <utility>

int main()
{
    using std::pair;
    using std::declval;
    using X = decltype(declval<pair<int&&,int&&>>().first);
    auto f = [](auto && p) -> decltype(auto) //((decltype(p)&&)p).first)
    {
        return ((decltype(p)&&)p).first;
    };
    using Y = decltype(f(declval<pair<int&&,int&&>>()));
}

In this code, Y becomes an alias for int&. I believe it should be int&&. X is an alias for int&&, and I think it's doing the same thing. Also, if I replace the decltype(auto) with decltype(((decltype(p)&&)p).first) -- which is *exactly* the return expression -- Y becomes an alias for int&&. Seems fishy to me.
Comment 1 Eric Niebler 2015-02-01 05:33:19 UTC
I think this is user error. I was confused between the difference between decltype(x.y) and decltype((x.y)). It seems the decltype(auto) is semantically the same as decltype((x.y)) in this case. From that perspective, gcc is begin consistent.

As for why decltype((declval<pair<int&&,int&&>>().first)) is int& instead of int&&, I'm guessing it's because of some subtlety of rvalue references that I don't yet grasp. I'll leave this open on the off-chance that this really is a bug, and on the off-change that someone will explain to me why it is the way it is.

Sorry in advance if this is just noise.
Comment 2 Harald van Dijk 2015-02-01 22:36:59 UTC
decltype(declval<pair<int&&,int&&>>().first) is int&& because declval<pair<int&&,int&&>>().first is "an unparenthesized class member access", which just reports how the member "first" is declared.

decltype((declval<pair<int&&,int&&>>().first)) is int& because (declval<pair<int&&,int&&>>().first) is an lvalue, just like any other named rvalue reference.

It's just like when you have int&&r;, decltype(r) is int&&, decltype((r)) is int&.

But as far as I can tell, the return type should be deduced as int&&, not as int&, because the expression in the return statement is not parenthesised. N4140 says "The type deduced for the variable or return type is determined as described in 7.1.6.2, as though the initializer had been the operand of the decltype." ([dcl.spec.auto]p7), without anything about adding parentheses like you suggest. That should then cause an error, because an lvalue cannot be used in a function returning an rvalue reference. And that's what clang does.

Reduced:

  int&& i = 0;
  decltype(auto) j = i;

or

  decltype(auto) f(int&&r) { return r; }

should both give an error, and do with clang, but are silently accepted by gcc.
Comment 3 Jonathan Wakely 2015-03-05 12:19:26 UTC
(In reply to Harald van Dijk from comment #2)
> Reduced:
> 
>   int&& i = 0;
>   decltype(auto) j = i;
> 
> or
> 
>   decltype(auto) f(int&&r) { return r; }
> 
> should both give an error, and do with clang, but are silently accepted by
> gcc.

Confirmed. EDG rejects them both too.
Comment 4 Marek Polacek 2020-11-05 21:38:01 UTC
Now fixed via PR78209.