5

In C++, a function with signature void f(A& a) (non-template) only binds to a lvalue.

Is it possible for an rvalue to automatically cast itself to an lvalue?

This is what I tried:

#include <cassert>
#include <utility>

struct A {
    operator A&() && {
        return *this;
    }
    void mutate() {...}
};

This produces a compiler warning, "converting 'A' to a reference to the same type will never use a type conversion". But at least the operator is compiled and works, except that it needs to be explicitly called with the full .operator A&() syntax.

void f(A& a) { a.mutate(); }

A make_A() { return A{}; }

int main() {
    A a;

    f(a);  // ok
    f(make_A().operator A&());  // ok
    f(make_A());  // does not compile, implicit conversion to A& doesn't work
    f(std::move(a));  // does not compile
}

https://godbolt.org/z/oKM8n5z1E

A similar question was asked before Rvalue to lvalue conversion? that shows how to do this. In my case, I want to enable this, but only for a specific class.

Is there a trick or workaround to achieve this?

4
  • auto as_lvalue(std::string&& s) -> std::string& { return s; } would spoof the rvalue as being an lvalue. Kind of the opposite of std::move. Commented Aug 10 at 22:59
  • The solution to the current duplicate target is the same solution for this question. The fact that you want to implement the solution for a specific class doesn't seem to be a sufficiently different question. Commented Aug 11 at 8:44
  • 1
    Even if the solution is nominally the same, the problem is different and could have had a different solution, different in generality. The other question is hard to find; it doesn't even have a verb in the title. Commented Aug 12 at 6:40
  • @alfC Yeah, that's fair, it's a reasonably different question that happens to have the same answer. BTW, the argument that the other question is hard to find is a bad argument, since that's literally the purpose of duplicate closures; having multiple ways of asking the same question, but all of them pointing to one canonical :) Commented Aug 18 at 15:42

2 Answers 2

9

No. This can't be done automatically.

Some libraries (including the standard library) use an internal helper function when they need such a conversion:

template<class T>
constexpr T& as_lvalue(T&& t) { return static_cast<T&>(t); }
Sign up to request clarification or add additional context in comments.

4 Comments

Ok, good to know (and sad). I guess that could be just the same as T& as_lvalue(T&& t) { return t; }.
The simplified implicit move in return in C++23 makes this no longer compiler.
@HolyBlackCat, which version doesn't compile in C++23? why exactly?
The one you suggested: gcc.godbolt.org/z/o176YbW6P
3

Technically you can do that with help of virtual dispatch, but I wouldn't say you can benefit from that:

#include <iostream>

struct Derived;

struct Base {
    virtual operator Derived&() && = 0;
};

struct Derived : Base {
    operator Derived&() && override {
        return *this;
    }
};

void Foo(Derived&) {
    std::cout << "Converted";
}

int main() {
    Foo(static_cast<Base&&>(Derived{}));
}

Without it, such a conversion is directly prohibited (both explicit and implicit) by the class.conv.fct/note-1 (emphasis mine):

A conversion function is never invoked for implicit or explicit conversions of an object to the same object type (or a reference to it), to a base class of that type (or a reference to it), or to cv void. Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class.

The earlier draft (n4950) also gives the following rationale in class.conv.fct/footnote-97:

These conversions are considered as standard conversions for the purposes of overload resolution ([over.best.ics], [over.ics.ref]) and therefore initialization ([dcl.init]) and explicit casts ([expr.static.cast]).

2 Comments

Interesting technique. Also, the function doesn't need to be virtual for it to work: godbolt.org/z/4r6YjaYbb. But yeah, it is not helpful at the end because it is still explicit (needs a cast to the base class, which I might as well replace with an explicit function).
I wonder why the language will forbid this. I would find it helpful for types that represent "reference" and at the point of on-the-fly creation, they are accidentally rvalues, but they are semantically just l-value reference-like.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.