In C++, what categories (lvalue, rvalue, xvalue, etc.) can expressions that produce temporaries of class type fall into?

前端 未结 2 1055
孤街浪徒
孤街浪徒 2020-11-27 06:23

Here is some example code:

#include 

class Foo
{
public:
  explicit Foo(int x) : data(x) {};

  Foo& operator++()
  {
    data += 1;
            


        
相关标签:
2条回答
  • 2020-11-27 07:00

    Any expression in C++ is either an lvalue or an rvalue. Hence, you're asking for the classifications that are rvalues. For that, inspect the figure showing the tree of classifications, in the C++11 standard §3.10/1.

    Expression category taxonomy


    For more info (without delving into the standard) see What are rvalues, lvalues, ....


    Regarding

    “Are expressions like Foo(5) rvalues or prvalue”

    a prvalue is necessary an rvalue – for it couldn't very well be an lvalue.

    A prvalue “(“pure” rvalue) is an rvalue that is not an xvalue”, and an xvalue is “the result of certain kinds of expressions involving rvalue references” A constructor call does not produce an rvalue reference, hence it's not an xvalue. So the rvalue is a prvalue, a pure rvalue.

    0 讨论(0)
  • 2020-11-27 07:03

    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>().

    0 讨论(0)
提交回复
热议问题