2

In C++ it is impossible to bind an r-value argument to a non-const l-value reference. But I noticed that when I call on the r-value object the method returning *this it compiles somehow.

Example: Obliviously this snippet od code will not compile

    class X
    {
    };
    
    void bar(X&)
    {
    }

    int main()
    {
      foo(X{}); //  invalid initialization of non-const reference of type 'X&' from an rvalue of type 'X'
    }

But adding a simple method to X makes it compilable:

class X
{
   public:
   X& foo() { return *this; }  
};

void bar(X&)
{
}

int main()
{
  bar(X{}.foo());
}

Why does it work? Does it mean that after calling foo r-value object becomes l-value object? Is it safe to use such construction? Is there any other way to achieve similar effect without creating a new method (something like X.this)?

2
  • 2
    The result of the function (the X&) is not an rvalue. If you try to change foo to return an rvalue reference (X&&), the compiler will not let you.
    – molbdnilo
    Commented Jan 19, 2021 at 8:23
  • There's no such thing as "rvalue object" and "lvalue object". The terms "rvalue" and "lvalue" refer to expressions. This is commonly mis-taught.
    – M.M
    Commented Jan 19, 2021 at 8:38

1 Answer 1

3

As mentioned in a comment, foo returns an lvalue, not an rvalue, so passing it to bar is just fine.

If it feels strange that X{}.foo() returns an lvalue even if X{} itself is an rvalue, think of foo() as a function which (implicitly) takes this and returns something (which happens to be what this points to) by lvalue reference. Now, how does foo take this? Does it accept rvalue? Does it accept lvalues? Yes to both, but you could write things so as to allow only one usage (look for ref-qualified member functions here):

class X
{
   public:
   X& fool() &  { return *this; } // called on lvalue objects
   X& foor() && { return *this; } // called on rvalue objects
};

void bar(X&)
{
}

int main()
{
    X x;
  bar(x.fool());
  //bar(x.foor());   // fails
  //bar(X{}.fool()); // fails
  bar(X{}.foor());
}

However, pay attention to what you do:

#include <iostream>
#include <type_traits>
class X
{
   public:
   X& fool() &  { return *this; }
   X& foor() && { return *this; }
   ~X() { std::cout << "dtor\n"; }
};

void bar(X&)
{
}

int main()
{
  auto& xxx = X{}.foor();
  // use xxx, but X{} has been destroyed
  std::cout << "bye\n";
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.