Empirically determine value category of C++11 expression?
decltype
can return the declared type of an entity (hence the name), but can also be used to query the type of an expression. However, in the latter case the resulting type is 'adjusted' according to the value category of that expression: an lvalue expression results in an lvalue reference type, an xvalue in an rvalue reference type, and a prvalue in just the type. We can use this to our benefit:
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
In C++, what categories (lvalue, rvalue, xvalue, etc.) can expressions that produce temporaries of class type fall into?
Every expression is one, and only one, of:
- lvalue
- xvalue
- prvalue
The union of expressions that are lvalues and xvalues are known collectively as glvalues.
The union of expressions that are xvalues and prvalues are known collectively as rvalues.
Thus xvalue expressions are known both as glvalues and rvalues.
The handy diagram found in Alf's answer correctly illustrates the relationship I've described with words above, and is also found in section 3.10 of the C++ standards, versions C++11 and above.
Everything I've said above, I suspect the OP already knew, just from the wording of the title of this question.
Trivia:
Bjarne Stroustrup invented this classification of expressions, and in
doing so perhaps saved the entire rvalue reference proposal from
collapsing in the Core Working Group. I will be forever grateful.
What I'm adding is a way to discover for yourself which of the three bottom classification categories any expression falls into: lvalue, xvalue or prvalue.
#include <type_traits>
#include <typeinfo>
#include <iostream>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <typename T>
std::string
expression_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
__cxxabiv1::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += "const ";
if (std::is_volatile<TR>::value)
r += "volatile ";
if (std::is_lvalue_reference<T>::value)
r = "lvalue expression of type " + r;
else if (std::is_rvalue_reference<T>::value)
r = "xvalue expression of type " + r;
else
r = "prvalue expression of type " + r;
return r;
}
The above function can be used like:
std::cout << "some_expression is a " << expression_name<decltype(some_expression)>() << '\n';
And it will answer this OP's question. For example:
int main()
{
std::cout << "Foo(5) is a " << expression_name<decltype(Foo(5))>() << '\n';
std::cout << "Foo(5).get_addr() is a " << expression_name<decltype(Foo(5).get_addr())>() << '\n';
std::cout << "Foo(78) = Foo(86) is a " << expression_name<decltype(Foo(78) = Foo(86))>() << '\n';
std::cout << "++Foo(5) is a " << expression_name<decltype(++Foo(5))>() << '\n';
std::cout << "++(Foo(78) + Foo(86)) is a " << expression_name<decltype(++(Foo(78) + Foo(86)))>() << '\n';
std::cout << "Foo(78) + Foo(86) is a " << expression_name<decltype(Foo(78) + Foo(86))>() << '\n';
std::cout << "std::move(Foo(5)) is a " << expression_name<decltype(std::move(Foo(5)))>() << '\n';
std::cout << "std::move(++Foo(5)) is a " << expression_name<decltype(std::move(++Foo(5)))>() << '\n';
}
For me prints out:
Foo(5) is a prvalue expression of type Foo
Foo(5).get_addr() is a prvalue expression of type void*
Foo(78) = Foo(86) is a lvalue expression of type Foo
++Foo(5) is a lvalue expression of type Foo
++(Foo(78) + Foo(86)) is a lvalue expression of type Foo
Foo(78) + Foo(86) is a prvalue expression of type Foo
std::move(Foo(5)) is a xvalue expression of type Foo
std::move(++Foo(5)) is a xvalue expression of type Foo
One area to be careful of in the use of this function:
decltype(variable_name)
will give the declared type of the variable name. If you want to discover the value category of the expression when variable_name
is used (as opposed to its declared type), then you need to add extra parentheses around (variable_name)
when used in decltype
. That is:
decltype((variable_name))
is the type of the expression variable_name
, and not the declared type of variable_name
.
For example given:
Foo&& foo = Foo(5);
std::cout << "foo is a " << expression_name<decltype(foo)>() << '\n';
This will erroneously output:
foo is a xvalue expression of type Foo
Add the extra parentheses to the decltype
:
std::cout << "foo is a " << expression_name<decltype((foo))>() << '\n';
to convert foo
from a type name into an expression. Now the output is:
foo is a lvalue expression of type Foo
If you are unsure whether you need to add parentheses or not to get the correct answer, then just add them. Adding them won't make a correct answer wrong -- unless you are looking to get the declared type of a variable, and not the type of an expression. And in that latter case, you want a closely related function: type_name<T>()
.
C++ what is the value category of *this?
From [basic.lval]:
An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment
expression) designates a function or an object. [ Example: IfE
is an expression of pointer type, then
*E
is an lvalue expression referring to the object or function to whichE
points. As another example,
the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
And from [expr.unary.op]:
The unary
*
operator performs indirection: the expression to which it is applied shall be a pointer to an
object type, or a pointer to a function type and the result is an lvalue referring to the object or function
to which the expression points.
Dereferencing a pointer is an lvalue. So *this
is an lvalue.
Alternatively, anything that isn't an lvalue is an rvalue. An rvalue is:
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated
with an object.
And *this
is definitely none of those things.
Is the reference returned from the function rvalue?
No. If a function returns an lvalue reference, then calling it gives you an lvalue.
If it returns an rvalue reference, you get an xvalue, and if it returns by value, you get a prvalue.
Also, strictly saying the result of calling the function is not a reference. It's an lvalue expression of type int
.
In general, you can determine value category of any expression by applying decltype
to it. It will add reference-ness to the type of the expression, indicating its category: &
for lvalues, &&
for xvalues, and nothing for prvalues.
Expressions themselves can't have reference types, so this added reference-ness will not conflict with their type.
For example, decltype(fun())
is int &
, proving that it's an lvalue.
Even though doesn't apply in this case, note that decltype
handles variable names differently from any other expressions (giving you the type of the variable, rather than expression type + value category). The workaround is to add a second set of (...)
around the variable name, which makes decltype
treat it as any other expression.
How to distiguish between an rvalue and rvalue reference in a function parameter
I don't see how to do this solely using function parameter. The distinction between xvalue and prvalue may be lost in the function call.
But you can do it with a macro that calls decltype
on the argument, before calling the function. Here is an example that calls your function with the relevant information as a second parameter. I borrowed code from this thread.
#include <iostream>
int rvalue()
{
return 1;
}
int&& rvalue_ref(int &&i) // Modified signature to avoid return reference to local variable (ty. user657267)
{
return std::move(i);
}
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
#define f(X) f_(X, VALUE_CATEGORY(X))
template<class T>
void f_(T&& x, char const *s)
{
std::cout << s << '\n';
}
int main()
{
f(rvalue()); // Should print "Not a reference"
f(rvalue_ref(1)); // Should print "Rvalue reference"
int j; f(j);
}
Output:
prvalue
xvalue
lvalue
Of course you can trivially modify the strings to suit, or replace them with enums etc.
Does the expression `new T` evaluate to an rvalue or an lvalue?
Short answer, I'm sure someone will write a longer one, but:
new
gives you a pointer rvalue: new int = nullptr;
fails to compile with error about requiring an lvalue.
Dereferencing that pointer gives you an lvalue: *(new int) = 5;
will compile (this naive statement also leaks memory because pointer is lost, of course).
The copy constructor takes a reference, so if you have pointer to object, you need to dereference it.
If you lose the pointer, then you can't delete it, so it won't get destructed and the heap memory will not get freed (until the program exits and memory is returned to the OS).
If you put the pointer to some other object which can take ownership of it, like a shared_ptr
, then you do not lose the pointer. The other object will delete it according to its semantics, at the latest when the other object (or the last one, in a case of shared ownership, like with shared_ptr
) itself gets destructed.
Narrowing conversions in C++11: what is the actual value after conversion?
This is a defect in the standard, see CWG issue 1449. The text has been changed to
from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type
Note: the issue's status, DRWP, means that officially, the standard has not yet been changed, and an argument can be made that at least your int64_t
example is legal in C++11. Compilers already implement the new rules, though, as this was already the intended meaning of the original wording.
Does `const &&` bind to all prvalues (and xvalues)?
T const&&
can bind to rvalues of type T
or const T
.
From 8.5.3 [dcl.init.ref] paragraph 5:
5 - A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows: [...]
— Otherwise, [...] the reference shall be an rvalue reference. [...]
— If the initializer expression
— is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2" [...]
then the reference is bound to the value of the initializer expression [...]
If the initializer expression is a prvalue of non-class type, then a temporary copy is created for reference binding (ibid).
Reference-compatibility is defined in 8.5.3p4; it requires a same-or-base-class relationship and same-or-greater cv qualification.
So for an rvalue to bind to T const&&
, its cv-qualification must be no greater than const
.
std::move of string literal - which compiler is correct?
This looks like a Visual Studio bug. This comes down to the std::move and if we look at the cppreference page it has the following signature:
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
and it returns:
static_cast<typename std::remove_reference<T>::type&&>(t)
which matches the draft C++ standard section 20.2.4
forward/move helpers [forward].
Using the code I grabbed from here we can see the following example:
#include <iostream>
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
int main()
{
std::cout << VALUE_CATEGORY( static_cast<std::remove_reference<const char[1]>::type&&>("") ) << std::endl ;
}
generates the following answer from gcc and clang using Wandbox:
xvalue
and this answer from Visual Studio using webcompiler:
lvalue
hence the error from Visual Studio for the original code:
You cannot bind an lvalue to an rvalue reference
when it attempt to bind the result of static_cast<typename std::remove_reference<T>::type&&>(t)
to std::remove_reference<T>::type&&
which is the return value of std::move
.
I don't see any reason why the static_cast
should generate an lvalue as it does in the Visual Studio case.
Related Topics
Differencebetween Packaged_Task and Async
Convert a Char* to Std::String
Iterate Through a C++ Vector Using a 'For' Loop
Using 3Rd Party Header Files with Rcpp
How to Make Objective-C Class Using Std:Vector Made Available to Swift Classes
How to Copy the Contents of an Array to a Std::Vector in C++ Without Looping
Benefits of Header-Only Libraries
How to Rearrange a String Equation
C++ Inherit from Multiple Base Classes with the Same Virtual Function Name
Libpng, Palette Png with Alpha or Not
Understanding the Example on Lvalue-To-Rvalue Conversion
Unoptimized Clang++ Code Generates Unneeded "Movl $0, -4(%Rbp)" in a Trivial Main()
Undefined Behaviour Somewhere in Boost::Spirit::Qi::Phrase_Parse
What Is the Best Modern C++ Approach to Construct and Manipulate a 2D Array
Why Is the C++ Stl Is So Heavily Based on Templates? (And Not on *Interfaces*)