Chapter introduction

This chapter builds on top of the concepts from lesson 1.9 -- Introduction to literals and operators. A quick review follows:

In mathematics, an operation is a mathematical calculation involving zero or more input values (called operands) that produces a new value (called an output value). The specific operation to be performed is denoted by a construct (typically a symbol or pair of symbols) called an operator.

For example, as children we all learn that *2 + 3* equals *5*. In this case, the literals *2* and *3* are the operands, and the symbol *+* is the operator that tells us to apply mathematical addition on the operands to produce the new value *5*.

In this chapter, we’ll discuss topics related to operators, and explore many of the common operators that C++ supports.

Operator precedence

Now, let’s consider a more complicated expression, such as *4 + 2 * 3*. In order to evaluate this expression, we must understand both what the operators do, and the correct order to apply them. The order in which operators are evaluated in a compound expression is determined by an operator’s precedence. Using normal mathematical precedence rules (which state that multiplication is resolved before addition), we know that the above expression should evaluate as *4 + (2 * 3)* to produce the value 10.

In C++, when the compiler encounters an expression, it must similarly analyze the expression and determine how it should be evaluated. To assist with this, all operators are assigned a level of precedence. Operators with the highest level of precedence are evaluated first.

You can see in the table below that multiplication and division (precedence level 5) have more precedence than addition and subtraction (precedence level 6). Thus, *4 + 2 * 3* evaluates as *4 + (2 * 3)* because multiplication has a higher level of precedence than addition.

Operator associativity

What happens if two operators in the same expression have the same precedence level? For example, in the expression *3 * 4 / 2*, the multiplication and division operators are both precedence level 5. In this case, the compiler can’t rely upon precedence alone to determine how to evaluate the result.

If two operators with the same precedence level are adjacent to each other in an expression, the operator’s associativity tells the compiler whether to evaluate the operators from left to right or from right to left. The operators in precedence level 5 have an associativity of left to right, so the expression is resolved from left to right: *(3 * 4) / 2 = 6*.

Table of operators

The below table is primarily meant to be a reference chart that you can refer back to in the future to resolve any precedence or associativity questions you have.

Notes:

- Precedence level 1 is the highest precedence level, and level 17 is the lowest. Operators with a higher precedence level get evaluated first.
- L->R means left to right associativity.
- R->L means right to left associativity.

Prec/Ass | Operator | Description | Pattern |
---|---|---|---|

1 None |
:: :: |
Global scope (unary) Namespace scope (binary) |
::name class_name::member_name |

2 L->R |
() () () {} type() type{} [] . -> ++ –– typeid const_cast dynamic_cast reinterpret_cast static_cast sizeof… noexcept alignof |
Parentheses Function call Initialization Uniform initialization (C++11) Functional cast Functional cast (C++11) Array subscript Member access from object Member access from object ptr Post-increment Post-decrement Run-time type information Cast away const Run-time type-checked cast Cast one type to another Compile-time type-checked cast Get parameter pack size Compile-time exception check Get type alignment |
(expression) function_name(parameters) type name(expression) type name{expression} new_type(expression) new_type{expression} pointer[expression] object.member_name object_pointer->member_name lvalue++ lvalue–– typeid(type) or typeid(expression) const_cast<type>(expression) dynamic_cast<type>(expression) reinterpret_cast<type>(expression) static_cast<type>(expression) sizeof…(expression) noexcept(expression) alignof(Type) |

3 R->L |
+ - ++ –– ! ~ (type) sizeof co_await & * new new[] delete delete[] |
Unary plus Unary minus Pre-increment Pre-decrement Logical NOT Bitwise NOT C-style cast Size in bytes Await asynchronous call Address of Dereference Dynamic memory allocation Dynamic array allocation Dynamic memory deletion Dynamic array deletion |
+expression -expression ++lvalue ––lvalue !expression ~expression (new_type)expression sizeof(type) or sizeof(expression) co_await expression &lvalue *expression new type new type[expression] delete pointer delete[] pointer |

4 L->R |
->* .* |
Member pointer selector Member object selector |
object_pointer->*pointer_to_member object.*pointer_to_member |

5 L->R |
* / % |
Multiplication Division Modulus |
expression * expression expression / expression expression % expression |

6 L->R |
+ - |
Addition Subtraction |
expression + expression expression - expression |

7 L->R |
<< >> |
Bitwise shift left Bitwise shift right |
expression << expression expression >> expression |

8 L->R | <=> | Three-way comparison | expression <=> expression |

9 L->R |
< <= > >= |
Comparison less than Comparison less than or equals Comparison greater than Comparison greater than or equals |
expression < expression expression <= expression expression > expression expression >= expression |

10 L->R |
== != |
Equality Inequality |
expression == expression expression != expression |

11 L->R | & | Bitwise AND | expression & expression |

12 L->R | ^ | Bitwise XOR | expression ^ expression |

13 L->R | | | Bitwise OR | expression | expression |

14 L->R | && | Logical AND | expression && expression |

15 L->R | || | Logical OR | expression || expression |

16 R->L |
throw co_yield ?: = *= /= %= += -= <<= >>= &= |= ^= |
Throw expression Yield expression Conditional Assignment Multiplication assignment Division assignment Modulus assignment Addition assignment Subtraction assignment Bitwise shift left assignment Bitwise shift right assignment Bitwise AND assignment Bitwise OR assignment Bitwise XOR assignment |
throw expression co_yield expression expression ? expression : expression lvalue = expression lvalue *= expression lvalue /= expression lvalue %= expression lvalue += expression lvalue -= expression lvalue <<= expression lvalue >>= expression lvalue &= expression lvalue |= expression lvalue ^= expression |

17 L->R | , | Comma operator | expression, expression |

You should already recognize a few of these operators, such as +, -, *, /, (), and sizeof. However, unless you have experience with another programming language, the majority of the operators in this table will probably be incomprehensible to you right now. That’s expected at this point. We’ll cover many of them in this chapter, and the rest will be introduced as there is a need for them.

Q: Where's the exponent operator?

C++ doesn’t include an operator to do exponentiation (operator^ has a different function in C++). We discuss exponentiation more in lesson 5.3 -- Modulus and Exponentiation.

Parenthesization

In normal arithmetic, you learned that you can use parentheses to change the order of application of operations. For example, we know that *4 + 2 * 3* evaluates as *4 + (2 * 3)*, but if you want it to evaluate as *(4 + 2) * 3* instead, you can explicitly parenthesize the expression to make it evaluate the way you want. This works in C++ because parentheses have one of the highest precedence levels, so parentheses generally evaluate before whatever is inside them.

Now consider an expression like *x && y || z*. Does this evaluate as *(x && y) || z* or *x && (y || z)*? You could look up in the table and see that && takes precedence over ||. But there are so many operators and precedence levels that it’s hard to remember them all. In order to reduce mistakes and make your code easier to understand without referencing a precedence table, it’s a good idea to parenthesize any non-trivial compound expression, so it’s clear what your intent is.

Best practice

Use parentheses to make it clear how an expression should evaluate, even if they are technically unnecessary.

Quiz time

Question #1

You know from everyday mathematics that expressions inside of parentheses get evaluated first. For example, in the expression `(2 + 3) * 4`

, the `(2 + 3)`

part is evaluated first.

For this exercise, you are given a set of expressions that have no parentheses. Using the operator precedence and associativity rules in the table above, add parentheses to each expression to make it clear how the compiler will evaluate the expression.

Sample problem: x = 2 + 3 % 4
Binary operator % has higher precedence than operator + or operator =, so it gets evaluated first: x = 2 + (3 % 4) Binary operator + has a higher precedence than operator =, so it gets evaluated next: Final answer: x = (2 + (3 % 4)) We now no longer need the table above to understand how this expression will evaluate. |

a) x = 3 + 4 + 5;

b) x = y = z;

c) z *= ++y + 5;

d) a || b && c || d;

5.2 -- Arithmetic operators |

Index |

4.x -- Chapter 4 summary and quiz |

#include <iostream>

int main () {

int x,y,z;

x=1; y=2, z=3;

(x = y) = z; // does x get set as target for z?

std::cout<<x<<'\n';

std::cout<<y<<'\n';

std::cout<<z<<'\n';

return 0;

}

output

3

2

3

I may be alone here, but I feel like your given answers to samples 'a' and 'b' confuse the recommendation to use parenthesis for non trivial expressions.

a+b+c is trivial in that it evaluates the same no matter what order you evaluate it. Associativity in C++ may dictate a left to right evaluation, but with addition operators you get the same answer when you evaluate right to left as well as if you were to start with b. I feel like no one would actually use parentheses in expressions with multiple + or = operators

I guess I just feel you should use different operators contained within the same precedence and associativity for 'a' and 'b' to remain in keeping with your recommendations.

I just realized that the 6th Precedence level only contains addition and subtraction, so there is no way to avoid trivial parenthesis placement on example 'a'. And it's still useful to remember that addition and subtraction are processed left to right.

Example 'b' is useful in knowing that expressions containing multiple '=' operators evaluate right to left. This is because in an expression like a=b=c, b=c will be evaluated first and evaluating a=b first will likely change the result.

Consider: [code]

int A{2};

int B{3};

int C{4};

a = b = c;

[code]

If associativity were left to right, A would be assigned the value 3 and then A would be assigned the value 4, but B would retain its original value of 3. Since associativity for '=' is right to left, first B would be reassigned the value 4, then A would also be reassigned the value of 4. This is important to remember.

If anyone knows any other implications of this functionality that I missed or got wrong, please let me know.

in the lesson: 2.3 — Introduction to function parameters and arguments

you said "if the arguments are function calls, then this can be problematic" and you used this sample to explain it

now according to this table, it says the associativity of the function call operator which is "parentheses" is left to right so it should evaluate function a() first in the above sample then b, etc I'm i right, or did I miss something?

Associativity only inserts invisible parentheses. For

Associativity has no effect on other expressions, such as separate arguments.

How come level 1 is the highest precedence an 17 is the lowest? shouldn't it be the other way around?

Because 1 goes first.

Well said

good for you

imposible

a || b && c || d;

Hide Solution

Binary operator && has higher precedence than ||:

// Even though it has higher precedence whole expression is treated as:

a || (b && c || d);

// a as left operand and remaining as right operand

// if 'a' results in true then short-circuit occurs right operand is not evaluated.....

Final answer: (a || (b && c)) || d;

// It should be :

(((a) || b )&& c )|| d

How do you change the default avatar image here? I don't seem to find a way.

learncpp uses your avatar from gravatar.com based on your email address

I have a question. Take a look please.

Program 1:

Program 2:

As both of the above programs work, which oprerator should I prefer? '&&' or 'and'? Same for '||' and 'or'.

Use whichever you prefer, the alternative operators (eg. "and") are used less often.

thanks for sharing. nice

"Binary operator + has left to right association:"

Yet + is in the level 3 table where it says R->L?

The + in level 3 is unary plus, not binary plus (which is in level 6).

can you use namespace in code?

It is not advisable to use namespace in code since it can conflicts with other library. Though you can use them for tutorial & learning purpose, it's best to avoid namespace. :)

Great tutorial! I've added it to my "c++ references" bookmarks folder :)

1.

> Use parenthesis to make it clear how an expression should evaluate, even if they are technically unnecessary.

Should I do this for operators that are evaluated from left to right?

is a lot less readable than

2. Tiny mistake I spotted:

Under the "Parenthesization" heading:

> you can use _parenthesis_ to change

> because _parenthesis_ have one of the

> so _parenthesis_ generally evaluate

In the "Best practice" block:

> Use _parenthesis_ to make it clear

"parenthesis" should be changed to "parentheses" in these 4 places, since:

parenthesis: singular

parentheses: plural.

3. Another thing:

"Name (required)", "Email (will not be published) (required)" and "Website" appear under the text boxes on mobile. This could explain why some people use the "Name (required)" as a "Subject" box. They probably think "Name (required)" is associated with the text box below it. [Here's a potential fix](https://imgur.com/a/xzn83Zl)

1.

No, if the order doesn't matter, you don't need parentheses.

2. Thanks

3. That's ugly, I like your fix. This is a job for Alex.

Alright, thank you

wow, your post is great makes me nostalgic learning C ++, I happen to be currently looking for additional information

I've been meaning to ask this for some time, For example we are defining a variable i:

and I've seen many instances of

why and when do we use these two? or is it just what we're comfortable with

I might have missed the explanation, if that's the case please excuse me.

Thanks :)

They both do the same. We use

when we want @i to be initialized, but override its value before using it for the first time, eg.

We do this to make sure we're never accessing an uninitialized variable, because that would cause undefined behavior.

If we use the initial value of @i, we explicitly initialize it to that value so that even people who don't know list initialization understand what value @i has in our code.

Is there any problem if we use:

for consistency. Also, thanks for the tutorial, reallly helps.

It might confuse someone because you're initializing `i` to a specific value but then never use it. There are also types for which you can't or shouldn't specify a default value, so you'll have

anyway, which breaks your consistency.

ok thanks that cleared up all my confusions.

Found a typo in the reference table, precedence level 8:

expresstion <=> expression

Table updated, thanks!

nascardriver or Alex, the operator precedence table at https://en.cppreference.com/w/cpp/language/operator_precedence

looks a bit different than the one you have here.

Not sure which one is more correct/updated, only pointing it out.

(I was trying to find out which one is the ternary operator, but ended up finding more than that)

I moved `throw` into precedence level 16 and added the missing operators. Is there anything more that you noticed in this table?

Thanks!

Nope, nothing else other than the fact that yours looks more organised as it shows the operators more clearly on separate rows. Thanks

Who are you why this non-sence?

Why do we have "Bitwise shift left/right" operators but not "insertion and extraction operator" in the chart above?

It's the same operator. For custom types, like streams, operators can be changed to do whatever the developer wants. For streams, the behavior is usually changed to input and output. We cover this later when we talk about operator overloading.

z *= ++y + 5;

++y is pre-increment. the pattern is ++lvalue. It has lower precedence than unary operator +.

y++ is post-increment. the pattern is lvalue++. It has higher precedence than unary operator +.

*= the lowest precedence.

Therefore:

z *= (++(y + 5));

Please correct me if I'm wrong. Thank you very much.

Lower precedences get executed first. ++y get executed before `+ 5`. The correct order is

Apologies, I'm getting lost here. In the table section Alex says that:

"Precedence level 1 is the highest precedence level, and level 17 is the lowest. Operators with a higher precedence level get evaluated first."

Using this example ++y and '+5' are both level 3 and are evaluated R->L so why doesn't '+5' get evaluated before '++'?

You looked at the unary plus operator, which has precedence 3, but the `+` in this example is binary (It has 2 operands). The binary plus (Addition) has precedence 6.

6 (a+b) is a lower precedence than 3 (++a)

Thanks for the explanation - now I get it!

I think that I'll just make sure that I use brackets to ensure the correct order of execution :)

I think + in this case is in the precedence level 6 since the unary operator is for identifying if a number is positive or negative. So the order is:

z *= ((++y) + 5)

what about ambiguity in SLR,LR and operator precedence