Intended invalid intialization from rvalue using *this

94 Views Asked by At

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)?

1

There are 1 best solutions below

0
On BEST ANSWER

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";
}