Does each expression in C++ have a non-reference type
Asked Answered
A

5

7

Hi i am reading about expression in C++ and across the statement

Statement 0.0

Each expression has some non-reference type

The quoted statement is from en.cppreference.com/w/cpp/language/value_category. Check line 2 at the top of the page.

Now i took some examples to understand what this means. For example:

int i = 100; // this expression has type int
int &j = i; // this expression has type int or int&?

My confusion is that i know that j is a reference to int that is j is int& but according to the quoted statement every expression has a non-reference type will imply that int &j = i; has type int. Is this correct? Other examples that i am getting confused about:

int a[4] = {2,4,4,9};
a[3]; // will this expression be int& type or int type?

Now in the statement a[3]; i know that a is a array lvalue and so a[3] returns a lvalue reference to the last element. But getting confused about will the quoted statement 0.0 imply that this whole expression a[3]; be a int or an int& type?

Here is another example:

b[4]; // Here assume that b is an array rvalue. So will this expression has  type int&& or int?

So my question is that does something similar happen for pointers also? Meaning do we have a similar statement(0.0) for pointers also?

int x = 34;
int *l = &x; // will this expression have type int* or int?

I now that here l is a pointer to int(compound type). If there is no similar statement for pointers then what is the need for this statement for references? That is why do we strip off the reference part only?

Automata answered 6/5, 2021 at 9:16 Comment(8)
Where does that quoted statement come from? As is, as you noted, doesn't appear to be true.Quiddity
Where is it written like that? Can you please share the link?Toucan
The quoted statement is from en.cppreference.com/w/cpp/language/value_category. Check line 2 at the top of the page.Automata
The whole phrase reads: "Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories: prvalue, xvalue, and lvalue." That does, instead, make more sense.Quiddity
The two halves of the complete statement you quoted are joined by "and" which means they should be meaningful separately as well. @QuiddityAutomata
The "(??)" following that sentence on the site suggests that even the author isn't sure about how correct it is. (Note that the site is not an official reference.)Unguiculate
@JasonLiam the interpretation is that a type like "int&" has type "int" and is an lvalue, "int&&" has type int and is an xvalue, and "int" has type "int" and is a prvalue. If you shorten the phrase as you did one it tempted to interpret it has an epxression has only a non-reference type.Quiddity
@DanielLangr can you explain/answer this?Automata
I
8
int i = 100; // this expression has type int
int &j = i; // this expression has type int or int&?

These statements are not expressions at all. These are declararions. They do contain sub expressions 100 and i, both of which have the type int. If you used the id expression j after this declaration, the type of that expression would be int.

So my question is that does something similar happen for pointers also?

No. Pointers are non-reference types, and something similar doesn't happen to expressions with pointer types.

why do we strip off the reference part only?

This is simply how the language works. It allows us treat objects and references to objects identically.

This is part of why you dont need to (nor can you) explicitly use an indirection operator to access the referred object, unlike needing to use an indirection operator to access a pointed object.


Here is the actual language rule (from latest standard draft):

[expr.type]

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.

Ito answered 6/5, 2021 at 9:36 Comment(0)
P
2

I have come to the following conclusions. To begin with, note that the type of an entity is only about what you can do with this entity, for example, addition is defined for integer types, but not for all class types.

As the question states, an expression can only have a non-reference type, i.e. can be either a fundamental type, a pointer type, a function type, ..., an array type, or a class type, but not a reference type. Which means that the available options for the type of expression listed above allow us to describe everything so that we can do everything with this expression that is possible to do with it in C++. That is, even in situations where we might expect the type to be a reference type, for example,

T a;
static_cast<T&>(a); // suspicious expression 

or

T a;
T& f(int &a) { return a; }
f(a); // another suspicious expression

we cannot do anything with this expressions that we could not do with an expression of the non-reference type T and of the same value category.

Note, that we can write

int a; 
static_cast<int&>(a)=3;

but the program containing following lines will be ill-formed,

int b; 
static_cast<int>(b)=3; // error: lvalue required as left operand of assigment.

because the ability to stand on the left side of an assignment is determined by the value category. Thus, describing an expression with a type and a value category, as opposed to describing it with a type alone, removes the need for a separate reference type, because that property is already described by the value category alone.

Plaintive answered 8/1, 2023 at 12:51 Comment(0)
A
1

Expression : An expression is made up of one or more operands and yields a result when it is evaluated.

Note the wording " when it is evaluated ".

Examples of expressions are: the literal 4, or some variable n. Again note that the expression is not yet evaluated and so have no result. Also you can create more complicated expressions from an operator and one or more operands. For example 3 + 4*5 is an unevaluated-expression. An expression with two or more operators is called a compound expression.

Each expression in C++ is either a rvalue or a lvalue.

Expression Statement: Statements end with a semicolon. When we add a semicolon to an expression, it becomes an expression statement. The affect of this is that this causes the expression to be evaluated and its result discarded at the end of the statement. So for example the literal 5 is an expression but if you add a semicolon ; after it then we will have a expression statement. Also the result of this expression will be discarded at the end of the expression statement. Lets looks at another example, cout << n; is a expression statement as a whole. It consists of the following expressions:

1. expression `cout`
2. expression 'n'

And it consists of one operator << and a null statement ; This whole statement will cause a side-effect which will be the printing of value of n on the screen.

Update:

Example 1: std::cout << n; will have a side-effect of printing the value of n on the screen but more importantly it will also have a resulting value which will be the object std::cout which is discarded at the end of the statement.

Example 2: int i(20 + 1); consists of 3 things:

  1. type : int
  2. identifier i
  3. expression 20 + 1

Example 3: float p; This has no expression. This is just variable definition. Also called declaration statement.

Example 4: float k = 43.2; This has an expression at the right hand side which is 43.2, a type float and an identifier k.

Example 5: i = 43;. This is an expression statement. There are two expressions and one operator here. The result is the variable i.

Example 6: int &r = i;. This is a declaration statement since it consists of the expression i on the right hand side. Also on the left hand side we have a type(int) and a declarator(&r). Since it is not an expression statement there will be no value that will be discarded.

Example 7: int *p = &i; This is an declaration statement since it consists of the expression i on the right hand side. Also on the left hand side we have a type(int) and a declarator(*p). Since it is not an expression statement there will be no value that will be discarded.

Example 8: i = a < b ? a : b; This is an expression statement. Here the expressions are:

  1. the left hand side variable i

  2. the variable a on the right hand side

  3. the variable b on the right hand side

Also there is one condition in the middle(a < b ). If the condition evaluates to true then the result of this will be the variable a and b otherwise.

Automata answered 6/5, 2021 at 11:23 Comment(10)
int &r = i; is a definition, and therefore cannot possibly be an expression statement. It does contain an initializer expression, however. Since it is not an expression statement, there is no value that is discarded. Same for int *p = &i.Fontenot
Yes they both are declaration statements. So nothing will be discarded.Automata
int &r = i; - So the & is a part of declarator not part of the type?Tonina
@John Yes, this is why the following works: int i = 0; int &ref = i, AnRef = 43;. Note also that the type of the object ref is still int& but the type of the expression ref is int.Automata
Are the same is also applied in the case of rvalue references? I mean: const int&& rr = 0; - the type of the object rr is const int&&. But if rr appears as an expression somewhere, then the type of the expression rr is const int. Correct?Tonina
@John Yes when rr is used as/in an expression then the type of that expression is const int.Automata
Thanks - Indeed you mean the type of the expression rr is const intTonina
@John You're welcome. Yes, the type of the expression rr in your example is const int and not int while the type of the object rr is const int&&Automata
But I think that it's not totally correct to say a reference is an object. Right? A reference is just a reference of some type, not an object though.Tonina
@John Yes, see(refer to) this question: Contradicting definition of references which i asked some time ago.Automata
D
1

I also encountered the same question and above answers don't solve it. But I find a post written by SCOTT MEYERS which discusses the question.

In a nutshell, if you take the standard literally then it is true that an expression can have reference type but may not very useful for understanding the language.

Dashpot answered 15/12, 2022 at 15:26 Comment(0)
F
0

C++ expressions can have forms like j+0 or (j). These expressions definitely have type int.

To simplify the grammar, the name of a variable by itself is also usable as an expression. If we didn't have this rule, in the grammar we'd have a lot of variable-or-expression constructs. But it means that the expression j has the same type as the expression j+0, namely int.

Fontenot answered 6/5, 2021 at 9:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.