4

I have a bit confusion about this code:

struct A
{
    A& bar()&&;
};

A& A::bar()&&
{
    std::cout << "A::bar()&&\n";
    return *this;
}

int main()
{
    A{}.bar();// called by an rvalue
}

So what I understand is that bar can be called only by a modifiable-rvalue. Until this it is OK. But how can bar return a non-constant lvalue reference to that rvalue?

  • How bar() binds and returns a modifiable lvalue reference to that rvalue object?
Ðаn
  • 10,934
  • 11
  • 59
  • 95
Maestro
  • 2,512
  • 9
  • 24
  • 4
    It just can. C++ lets you shoot yourself in the foot if you want to. You can also *reinterpret_cast(0x1234)++ if you insist. – einpoklum Mar 11 '20 at 21:20
  • 1
    Hmm, I wonder if we can consider this a defect with the language. It would be cool if we could have `*this` resolves to `T&&` if the function is `&&` qualified. – NathanOliver Mar 11 '20 at 21:22

2 Answers2

3

The reason is that the this pointer for a class C can be either C* or const C* - not C& * or C&& * (those aren't actual types; you can't declare a C& * ptr). So, even when your method runs for an rvalue instance of class A, you get one of those two (GodBolt). And when you apply the * operator, you get an lvalue, not an rvalue.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Thank you so much! – Maestro Mar 11 '20 at 21:42
  • 1
    @Maestro: We should both thank [Howard Hinnant and @康桓瑋 for developing the neat `type_name()` function](https://stackoverflow.com/q/81870/1593077). It should really get more attention IMHO. – einpoklum Mar 11 '20 at 21:44
2

This has to do with [expr.unary.op]/1

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. [ Note: Indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see [conv.lval]. — end note ]

emphasis mine

So when you dereference this yo get an lvalue. It doesn't matter if this is pointing to a temporary object or not, you will always get an lvalue. Since *this is an lvalue, you are legally allowed to return an lvalue reference, the program in syntactically correct. Semantically it is not, but that is a lot harder to test for and is often not something that is diagnosed as it requires quite a bit of static analysis.


It would be cool if the language could be updated where * only yields an lvalue when applied to this in a non-rvalue qualified function.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Really amazing! So this because "dereference operator" yields an lvalue thus it can be bound to a non-const lvalue reference? Another thing is that an rvalue-refrence itself is an lvalue although it refrs to an rvalue. Is this true? Thank you! – Maestro Mar 11 '20 at 21:40
  • 1
    @Maestro Yep to question 1. Yes, that is true, for question 2. **All** references are lvalues. Also anything with a name is an lvalue, except for `this` – NathanOliver Mar 11 '20 at 21:41
  • Ah ok! I missed the last one "about this". thank you. – Maestro Mar 11 '20 at 21:42