C++ Type and Value Category for Expression and Variable

前端 未结 3 537
死守一世寂寞
死守一世寂寞 2021-02-06 01:31

From this link, it says that

Objects, references, functions including function template specializations, and expressions have a property called type

<
相关标签:
3条回答
  • 2021-02-06 02:19

    Does rf_int have a reference type or not?

    The entity (or variable) with the name rf_int has type int&& (a reference type) because of the way it is declared, but the expression rf_int has type int (a non-reference type) per [expr]/5:

    If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression. [ Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see [basic.life]).  — end note ]


    Do we have to provide context when talking about the type of a name, be it a variable or an expression?

    Yes, we do. rf_int can be said to have different types depending on whether it refers to the entity or the expression.


    More specifically, when a variable name is used in function call:

    SomeFunc(rf_int);
    

    Is rf_int now considered an expression (thus it is an lvalue with type int), or a variable (thus it is an lvalue with type rvalue reference to int)?

    It is considered an expression, which is an lvalue of type int. (Note that value category is a property of expressions. It is not correct to say a variable is an lvalue.)

    0 讨论(0)
  • 2021-02-06 02:21

    Based on this each function call is an expression. Each argument passed to the function is also an expression.

    Therefore, when you make a call to SumFunc(rf_int); you create an expression from the single rf_int variable.

    ISO/IEC 14882 (c++14 standard) states that "The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression."

    Thus, the rf_int expression will be of type int.

    This comment (you mention it before) is about type conversion error.

    To illustrate my explanation, I have prepared a more complex, but (hopefully) more understandable example. This example is about why we need rvalue refernce type and how to use it correctly.

    class Obj {
        int* pvalue;
    public:
        Obj(int m) {
            pvalue = new int[100];
            for(int i = 0; i < 100; i++)
                pvalue[i] = m + i;
        }
        Obj(Obj& o) {  // copy constructor
            pvalue = new int[100];
            for(int i = 0; i < 100; i++)
                pvalue[i] = o.pvalue[i];
        }
        Obj(Obj&& o) {  // move constructor
            for(int i = 0; i < 100; i++)
                pvalue = o.pvalue;
            o.pvalue = nullptr;
        }
       // ...
    };
    

    Example 1

      Obj obj1(3);
      Obj obj2(obj1);  // copy constructor
      Obj obj3(std::move(obj1)); // move constructor
      Obj obj4(Obj(3)); // move constructor
    

    Line #1 - obj1 created.
    Line #2 - obj2 created as a copy of obj1
    Line #3 - obj3 created by moving value of obj1 to the obj3; obj1 loose it's value
    Line #4 - temporary object created by Obj(3); obj4 created by moving value of temporary object to obj4; temporary object loose it's value

    I think we have no real need of int&& but Obj&& can be very useful. Especially in the line #4.

    Example 2 with my (simplified) interpretation of compiler errors

    void SomeFunc0(Obj   arg) {};
    void SomeFunc1(Obj&  arg) {};
    void SomeFunc2(Obj&& arg) {};
    
    int main()
    {
      Obj   obj1(3);        // object
      Obj&  obj2 = obj1;    // reference to the object
      Obj&& obj3 = Obj(3);  // reference to the temporary object with extened lifetime
    
    
        SomeFunc0(obj1); // ok - new object created from Obj
        SomeFunc0(obj2); // ok - new object created from Obj&
        SomeFunc0(obj3); // ok - new object created from Obj&&
        SomeFunc0(Obj(3)); // ok - new object created from temporary object
        SomeFunc0(std::move(obj1)); // ok - new object created from temporary object
        SomeFunc0(std::move(obj2)); // ok - new object created from temporary object
        SomeFunc0(std::move(obj3)); // ok - new object created from temporary object
    
        SomeFunc1(obj1); // ok - reference to obj1 passed
        SomeFunc1(obj2); // ok - reference to obj1 passed
        SomeFunc1(obj3); // ok - reference to temp. obj. passed
        SomeFunc1(Obj(3)); // error - lifetime of the temp. obj. too short
        SomeFunc1(std::move(obj1)); // error - lifetime of the temp. obj. too short
        SomeFunc1(std::move(obj2)); // error - lifetime of the temp. obj. too short
        SomeFunc1(std::move(obj3)); // error - lifetime of the temp. obj. too short
    
        SomeFunc2(obj1); // error - temporary object required
        SomeFunc2(obj2); // error - temporary object required
        SomeFunc2(obj3); // error - lifetime of the temp. obj. too long
        SomeFunc2(Obj(3)); // ok
        SomeFunc2(std::move(obj1)); // ok
        SomeFunc2(std::move(obj2)); // ok
        SomeFunc2(std::move(obj3)); // ok
    
        return 0;
    }
    
    0 讨论(0)
  • 2021-02-06 02:30

    It confused me first too but let me clear out the ambiguity in a simple way.

    EXPRESSION is something that can be evaluated and must evaluate to a non-reference type right ?
    ( Yes, of course duh !! )

    Now we also know a variable name is an l-value EXPRESSION.
    ( Dude get to the point already and stop making the expression word bold )

    Okay now here is the catch, when we say a variable we mean a place in memory. Now will we call a place in memory an expression ? No, definitely not that is just completely absurd.

    Expression is a generic term that we identify by defining some rules and anything that fall under those rules is an expression. It is necessary to define it this way to make sense of the code during compiler construction. One of that rule is that anything that evaluates to a value is an expression. Since from coding perspective using a variable name means that you wish the actual value is used when the code is compiled, so we call that variable name an expression.

    So when they say a variable is an expression they don't mean the variable as in place in memory but the variable NAME from coding perspective. But using the term "variable name" to differentiate from the actual variable (place in memory) is just absurd. So saying "variable is an expression" is just fine as long as you think it from coding perspective.

    Now to answer this first :

    More specifically, when a variable name is used in function call:

    SomeFunc(rf_int);
    

    Is rf_int now considered an expression (thus it is an lvalue with type int), or a variable (thus it is an lvalue with type rvalue reference to int)?

    A single variable is also an expression. So this question becomes invalid.

    Now to answer this :

    Based on the above two statement, rf_int can be treated as an expression and expression has non-reference type.

    Now I am really confused. Does rf_int have a reference type or not?

    What if I say rf_int is an r-value reference and rf_int is also an l-value EXPRESSION.
    ( Oh brother, this guy and his obsession with expression )

    It is true because if you do the following it will work.

    int &&rf_int = 10;   // rf_int is an r-value reference 
    int &x = rf_int;     // x is an l-value reference and l-value reference can be initialized with l-value expression
    cout << x;           //Output will be 10
    

    Now is rf_int an expression or a r-value reference, what will it be ? The answer is both. It depends on from which perspective you are thinking.

    In other words what I'm trying to say is that if we think rf_int as a variable (some place in memory) then surely it has the type of r-value reference but since rf_int is also a variable name and from coding perspective it is an expression and more precisely an l-value expression and whenever you use this variable for the sake of evaluation you will get the value 10 which is an int so we are bound to say that rf_int type as an expression is an int which is a non-reference type.

    If you think from compiler perceptive for a moment what line of code evaluates to a reference ? None right ?. You can try searching if you find any do let me know as well. But the point here is that the type of expression doesn't mean type of variable. It means the type of value that you get after evaluating the expression.

    Hope I have clarified your question. If I missed something do let me know.

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