14

Recently I've discovered that sometimes being able to turn rvalues temporarily into lvalues can be useful for me.

I've been using the following tool:

#include <type_traits>

template <typename T>
inline constexpr std::remove_reference_t<T> &lvalue(T &&r) noexcept {
    return static_cast<std::remove_reference_t<T> &>(r);
}

It's useful when you have to use functions that require lvalues as arguments, but you don't have any interest in what those particular values get changed into. For when you are interested in other output vectors that are not related to the given specific argument.

For example, this:

std::string get_my_file() {
    std::ifstream ifs("myfile.txt");
    return {std::istreambuf_iterator<char>(ifs), {}};
}

can be changed to this:

std::string get_my_file() {
    return {std::istreambuf_iterator<char>(lvalue(std::ifstream("myfile.txt"))),
            {}};
}

And this:

std::string temp1 = get_my_shader();
const char *temp2 = temp1.c_str();
glShaderSource(a, 1, &temp2, nullptr);

can be changed to this:

glShaderSource(a, 1, &lvalue(get_my_shader().c_str()), nullptr);

And allow things like this:

void foo(int *x) {
    std::cout << *x << std::endl;
}

foo(&lvalue(5));

I'd like to be sure whether I'm invoking undefined-behavior or not in any of this, because I don't see any, although there may be some casting rule that would turn it illegal (which I ignore). Regarding lifetime of temporaries, I don't see a problem since, AFAIK, rvalues live until the end of full-expression and usage of the function is restricted to that.

There's a recent change to the standard about reinterpret_cast and xvalues that seems to be on topic:

https://stackoverflow.com/a/26793404/1000282

EDIT:

Better version using reference collapsing as suggested:

template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }
oblitum
  • 11,380
  • 6
  • 54
  • 120
  • You are fine. The only thing which you can easier screw up by casting xvalues to lvalues is lifetime issues, and you are right there's no danger in your examples, as the temporary is not referenced beyond the statement creating it. (The `reinterpret_cast`-proposal does not actually touch on your question.) – Deduplicator Nov 21 '14 at 02:37
  • @Deduplicator Thanks, I stated rvalues instead of specifically xvalues, because the question also includes expressions like `&lvalue(5)`. Which by the way is working fine. – oblitum Nov 21 '14 at 02:39
  • 1
    BTW: Why do you use `std::remove_reference_t` instead of reference-collapsing rules? – Deduplicator Nov 21 '14 at 02:44
  • @Deduplicator Because the code was based on libc++ `std::move` :-). Any terser improvement on it? I would like to know, I didn't care to think further how to turn it simpler, but I had a suspicion. – oblitum Nov 21 '14 at 02:46
  • 1
    Passing a prvalue works, because it is copied to a temporary, which is passed by xvalue-reference to your function, and that temporary will only be destroyed at the end of the full expression, like the rest. About the improvement in brevity: return-value instead of `std::remove_reference_t &` `T&`. BTW: I call mine `no_move`, and it's also useful for default-arguments. – Deduplicator Nov 21 '14 at 02:46
  • The casting is not a problem here, because casts between lvalue refs and rvalue refs, is well-defined in both directions. And for the life-time, you just have to be careful with that, use it conservatively, and document the life-time expectations correctly,.. which is pretty much the same caution you would apply when taking the address of a reference or const-reference. – Mikael Persson Nov 21 '14 at 02:47
  • @Deduplicator I guess reference collapsing would make `template inline constexpr auto &lvalue(T &&r) noexcept {return static_cast(r);}` work the same? – oblitum Nov 21 '14 at 02:59
  • 1
    @pepper_chico: Yes. BTW: The `static_cast` is also superfluous. – Deduplicator Nov 21 '14 at 03:00
  • @Deduplicator thanks for proof-checking. – oblitum Nov 21 '14 at 03:01
  • BTW: The `inline` is implied by `constexpr`. – Deduplicator Nov 21 '14 at 03:05
  • @Deduplicator ah, it came from libc++, since there, `constexpr` is not used, but a macro for it (for platforms where it may be absent). – oblitum Nov 21 '14 at 03:06
  • But where temporary from `lvalue(std::ifstream("myfile.txt"))` dies? Is there dangling reference? – Tomilov Anatoliy Nov 21 '14 at 08:19
  • @Orient it's an interesting question. It seems particularly not important in this case because the iterator is used just to construct the string object by copying data, and ifstream lives for that task at last, and it's not used afterwards anywhere. – oblitum Nov 21 '14 at 08:25
  • @Orient it should be dead at end of full expression, meaning on return of `get_my_file` it's dead, but string is already constructed with its own data. – oblitum Nov 21 '14 at 08:29

1 Answers1

9

As you said, you took care to not let any pointer or reference to the temporaries escape their scope.
Using your lvalue-function (mine is called no_move) makes it easier to break that stricture inadvertently.

Next, let's look at what xvalues are: Expiring objects, but objects nevertheless.
This means, you can ignore they are on their funeral tour (if you pass them to a function, that function will naturally do so, unless you ask to take advantage).

The last point you mentioned was calling with a prvalue, which is most certainly not an object.
But even that is not a problem, as on calling the function, a temporary will be created.
And that temporary will naturally also survive to the end of the statement.

As an aside, using std::remove_reference_t<T>& is unneccessary for the return-type of lvalue, you can use T& directly and rely on the reference-collapsing-rules. Also, the static_cast and inline are superfluous.

template <typename T> constexpr T& lvalue(T&& r) noexcept {return r;}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118