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; }
The values that are passed in the function call are called the actual parameters. The values received by the function (when it is called ) are called the formal parameters.
Arguments are Passed by Value The parameters, in a function call, are the function's arguments. JavaScript arguments are passed by value: The function only gets to know the values, not the argument's locations. If a function changes an argument's value, it does not change the parameter's original value.
Passing Functions as Arguments. Functions are data, and therefore can be passed around just like other values. This means a function can be passed to another function as an argument. This allows the function being called to use the function argument to carry out its action.
When there are multiple functions (which is most of the time), there needs to be a way to pass data between the functions. This is done by passing values in parenthesis: myFunction(myData). Even when there is no data to be passed, we still have to declare and execute functions by using parenthesis: myFunction().
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;}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With