31

Each expression in C++11 has a value category. One of lvalue, xvalue or prvalue.

Is there a way to write a macro that, given any expression as an argument, will produce a string "lvalue", "xvalue" or "prvalue" as appropriate?

For example:

int main()
{
    int x;

    cout << VALUE_CAT(x) << endl; // prints lvalue
    cout << VALUE_CAT(move(x)) << endl; // prints xvalue
    cout << VALUE_CAT(42) << endl; // prints prvalue
}

How could VALUE_CAT be implemented?

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • 1
    Something along the lines of `#define VALUE_CAT(expr) get_value_description(sizeof SFINAE_test_1((expr)), sizeof SFINAE_test_2((expr)))` – Ben Voigt May 19 '13 at 18:22
  • I came up with this but it doesn't think the second is a glvalue... http://ideone.com/ARlW3v – Ben Voigt May 19 '13 at 18:35
  • @BenVoigt I don't think an overload resolution based solution can work, because an overload set cannot distinguish an xvalue from a prvalue. It's too bad, because it might have been possible to avoid a macro altogether (e.g. overload set of `constexpr` functions). (Actually I'm really glad that's not the case, would make overload resolution more convoluted than it already is!) – Luc Danton May 19 '13 at 18:40

3 Answers3

51

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
Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • 3
    I didn't realize `decltype` "mapped" expression value categories (lvalue, xvalue, prvalue) to reference types (lvalue reference to T, rvalue reference to T, T). Thanks. – Andrew Tomazos May 19 '13 at 18:39
  • 1
    @user1131467: You have to use `decltype` with the expression wrapped in double-parenthesis like that. That changes how `decltype` deduces the expression's type. – Nicol Bolas May 19 '13 at 18:53
  • `decltype(...)` is documented in N3485 7.1.6.2/4 – Andrew Tomazos May 19 '13 at 19:12
  • @NicolBolas: Only if it is an `id-expression`. For example `decltype(move(x))` will produce `int&&` as expected. – Andrew Tomazos May 19 '13 at 21:16
  • @Nikos ["named rvalue references are lvalues"](https://stackoverflow.com/q/3601602). – user202729 Feb 07 '20 at 08:03
  • @Nikos: From https://en.cppreference.com/w/cpp/language/decltype: Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types. What you want is: `int x = 1;` `int&& foo = x++;` `static_assert(std::is_rvalue_reference::value);` – Alexander Jan 29 '21 at 16:39
1

You could also try using the clang API's Classification function to return the category of the expression from a clang AST containing the expression. This is, of course, far more complex than @Luc's solution, since it requires generating the actual AST through clang.

kgraney
  • 1,975
  • 16
  • 20
0
#ifndef _TPF_TYPE_NAME_H
#define _TPF_TYPE_NAME_H

template <typename T>
constexpr bool is_lvalue_helper = std::is_lvalue_reference<T>::value;

template <typename T>
constexpr bool is_xvalue_helper = std::is_rvalue_reference<T>::value;

template <typename T>
constexpr bool is_prvalue_helper = !(is_lvalue_helper<T> || is_xvalue_helper<T>);

template <typename T>
constexpr bool is_rvalue_helper = is_xvalue_helper<T> || is_prvalue_helper<T>;

template <typename T>
constexpr bool is_glvalue_helper = is_xvalue_helper<T> || is_lvalue_helper<T>;

#define is_lvalue(type_instance) is_lvalue_helper<decltype((type_instance))>
#define is_xvalue(type_instance) is_xvalue_helper<decltype((type_instance))>
#define is_prvalue(type_instance)is_prvalue_helper<decltype((type_instance))>
#define is_rvalue(type_instance) is_rvalue_helper<decltype((type_instance))>
#define is_glvalue(type_instance)is_glvalue_helper<decltype((type_instance))>

#endif // end of _TPF_TYPE_NAME_H
mada
  • 1,646
  • 1
  • 15