Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine programmatically if an expression is rvalue or lvalue in C++?

What's the best way to determine if an expression is a rvalue or lvalue in C++? Probably, this is not useful in practice but since I am learning rvalues and lvalues I thought it would be nice to have a function is_lvalue which returns true if the expression passed in input is a lvalue and false otherwise.

Example:

std::string a("Hello");
is_lvalue(std::string()); // false
is_lvalue(a); // true  
like image 892
Giuseppe Pes Avatar asked Mar 29 '16 22:03

Giuseppe Pes


People also ask

What is the difference between lvalue and rvalue in C?

An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address). rvalues are defined by exclusion. Every expression is either an lvalue or an rvalue, so, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.

Is pointer an rvalue or lvalue?

in the second line of your program, the expression &i is an expression of type int* , and is thusly an example of a pointer being treated as an rvalue.

What is the difference between lvalue and rvalue references?

“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable.

Can you pass an lvalue to an rvalue reference?

Explanation: If you pass an lvalue T to enqueue , U will deduce to T& , and the forward will pass it along as an lvalue, and you'll get the copy behavior you want. If you pass an rvalue T to enqueue , U will deduce to T , and the forward will pass it along as an rvalue, and you'll get the move behavior you want.

What is lvalue and rvalue in C language?

lvalue and rvalue in C language. L-value: “l-value” refers to memory location which identifies an object. l-value may appear as either left hand or right hand side of an assignment operator(=). l-value often represents as identifier. Expressions referring to modifiable locations are called “modifiable l-values“.

What is an rvalue in JavaScript?

An rvalue (pronounced “arr-value”, short for “right value”, and sometimes written as r-value) is an expression that is not an l-value. Commonly seen rvalues include literals (except string literals, which are lvalues) and the return value of functions or operators. Rvalues only exist within the scope of the expression in which they are used.

What is an lvalue in Python?

An lvalue (pronounced “ell-value”, short for “left value” or “locator value”, and sometimes written as “l-value”) is an expression that evaluates to a function or object that has an identity.

Why is it important to return lvalues from functions in C++?

Actually, the ability of C++ to return lvalues from functions is important for implementing some overloaded operators. One common example is overloading the brackets operator [] in classes that implement some kind of lookup access. std::map does this:


3 Answers

Most of the work is already done for you by the stdlib, you just need a function wrapper:

template <typename T>
constexpr bool is_lvalue(T&&) {
  return std::is_lvalue_reference<T>{};
}

in the case you pass a std::string lvalue then T will deduce to std::string& or const std::string&, for rvalues it will deduce to std::string

Note that Yakk's answer will return a different type, which allows for more flexibility and you should read that answer and probably use it instead.

like image 93
Ryan Haining Avatar answered Oct 19 '22 00:10

Ryan Haining


I solved the above question using two overloaded template functions. The first takes as input a reference to a lvalue and return true. Whereas the second function uses a reference to rvalue. Then I let the compiler match the correct function depending on the expression passed as input.

Code:

#include <iostream>

template <typename T>
constexpr bool is_lvalue(T&) {
    return true;
}

template <typename T>
constexpr bool is_lvalue(T&&) {
    return false;
}

int main()
{
    std::string a = std::string("Hello");
    std::cout << "Is lValue ? " << '\n';
    std::cout << "std::string() : " << is_lvalue(std::string()) << '\n';
    std::cout << "a : " << is_lvalue(a) << '\n';
    std::cout << "a+b : " << is_lvalue(a+ std::string(" world!!! ")) << '\n';
} 

Output:

Is Lvalue ? 
std::string() : 0
a : 1
a+b : 0
like image 33
Giuseppe Pes Avatar answered Oct 19 '22 00:10

Giuseppe Pes


I would take a page from boost::hana and make the return value of is_lvalue encode the lvalue-ness of its argument both as a constexpr value, and as a type.

This lets you do stuff like tag dispatching without extra boilerplate.

template<class T>
constexpr std::is_lvalue_reference<T&&>
is_lvalue(T&&){return {};}

the body of this function does nothing, and the parameter's value is ignored. This lets it be constexpr even on non-constexpr values.

An advantage of this technique can be seen here:

void tag_dispatch( std::true_type ) {
  std::cout << "true_type!\n";
}
void tag_dispatch( std::false_type ) {
  std::cout << "not true, not true, shame on you\n";
}

tag_dispatch( is_lvalue( 3 ) );

Not only is the return value of is_lvalue available in a constexpr context (as true_type and false_type have a constexpr operator bool), but we can easily pick an overload based on its state.

Another advantage is that it makes it hard for the compiler to not inline the result. With a constexpr value, the compiler can 'easily' forget that it is a true constant; with a type, it has to be first converted to bool for the possibility of it being forgotten to happen.

like image 21
Yakk - Adam Nevraumont Avatar answered Oct 18 '22 23:10

Yakk - Adam Nevraumont