I am trying to know the order of execution of functions inside the cout statement
I tried this set of codes
#include < iostream >
using namespace std;
int i=0;
int sum(int a)
{
i++;
return a+i;
}
int main()
{
cout << sum(3) << sum(2) ;
return 0;
}
"I expected the output to be 44, but the actual output is 53"
As stated here: https://en.cppreference.com/w/cpp/language/eval_order
Order of evaluation of any part of any expression, including order of
evaluation of function arguments is unspecified (with some exceptions
listed below). The compiler can evaluate operands and other
subexpressions in any order, and may choose another order when the
same expression is evaluated again.
There is no concept of left-to-right or right-to-left evaluation in
C++. This is not to be confused with left-to-right and right-to-left
associativity of operators: the expression a() + b() + c() is parsed
as (a() + b()) + c() due to left-to-right associativity of operator+,
but the function call to c may be evaluated first, last, or between
a() or b() at run time
In your line
cout << sum(3) << sum(2)
the order of the two operator<< calls depends on the operator you use (here << so left-to-right), but the evaluation of each subexpression, namely sum(3) and sum(2) has no defined order and depends on the mood (most optimized compile approach usually) of your compiler.
For info here is a list of operators associativity: https://en.cppreference.com/w/cpp/language/operator_precedence
Related
For example, we have
int p(void) {
return 4;
}
int q(void) {
return 5;
}
int main(void) {
int x = p() + q();
return 0;
}
How does the stack frame look like in this case? To be exact, are p and q evaluated simultaneously, or after p is first evaluated to be 4, the program proceeds to q?
From cppreference
Order of evaluation of the operands of almost all C++ operators
(including the order of evaluation of function arguments in a
function-call expression and the order of evaluation of the
subexpressions within any expression) is unspecified. The compiler can
evaluate operands in any order, and may choose another order when the
same expression is evaluated again.
There are exceptions to this rule which are noted below.
Except where noted below, there is no concept of left-to-right or
right-to-left evaluation in C++. This is not to be confused with
left-to-right and right-to-left associativity of operators: the
expression f1() + f2() + f3() is parsed as (f1() + f2()) + f3() due to
left-to-right associativity of operator+, but the function call to f3
may be evaluated first, last, or between f1() or f2() at run time
Why does the following print bD aD aB aA aC aU instead of aD aB aA aC bD aU? In other words, why is b-- evaluated before --++a--++?
#include <iostream>
using namespace std;
class A {
char c_;
public:
A(char c) : c_(c) {}
A& operator++() {
cout << c_ << "A ";
return *this;
}
A& operator++(int) {
cout << c_ << "B ";
return *this;
}
A& operator--() {
cout << c_ << "C ";
return *this;
}
A& operator--(int) {
cout << c_ << "D ";
return *this;
}
void operator+(A& b) {
cout << c_ << "U ";
}
};
int main()
{
A a('a'), b('b');
--++a-- ++ +b--; // the culprit
}
From what I gather, here's how the expression is parsed by the compiler:
Preprocessor tokenization: -- ++ a -- ++ + b --;
Operator precedence1: (--(++((a--)++))) + (b--);
+ is left-to-right associative, but nonetheless the compiler may choose to evaluate the expression on the right (b--) first.
I'm assuming the compiler chooses to do it this way because it leads to better optimized code (less instructions). However, it's worth noting that I get the same result when compiling with /Od (MSVC) and -O0 (GCC). This brings me to my question:
Since I was asked this on a test which should in principle be implementation/compiler-agnostic, is there something in the C++ standard that prescribes the above behavior, or is it truly unspecified? Can someone quote an excerpt from the standard which confirms either? Was it wrong to have such a question on the test?
1 I realize the compiler doesn't really know about operator precedence or associativity, rather it cares only about the language grammar, but this should get the point across either way.
The expression statement
--++a-- ++ +b--; // the culprit
can be represented the following way
at first like
( --++a-- ++ ) + ( b-- );
then like
( -- ( ++ ( ( a-- ) ++ ) ) ) + ( b-- );
and at last like
a.operator --( 0 ).operator ++( 0 ).operator ++().operator --().operator + ( b.operator --( 0 ) );
Here is a demonstrative program.
#include <iostream>
using namespace std;
#include <iostream>
using namespace std;
class A {
char c_;
public:
A(char c) : c_(c) {}
A& operator++() {
cout << c_ << "A ";
return *this;
}
A& operator++(int) {
cout << c_ << "B ";
return *this;
}
A& operator--() {
cout << c_ << "C ";
return *this;
}
A& operator--(int) {
cout << c_ << "D ";
return *this;
}
void operator+(A& b) {
cout << c_ << "U ";
}
};
int main()
{
A a('a'), b('b');
--++a-- ++ +b--; // the culprit
std::cout << std::endl;
a.operator --( 0 ).operator ++( 0 ).operator ++().operator --().operator + ( b.operator --( 0 ) );
return 0;
}
Its output is
bD aD aB aA aC aU
bD aD aB aA aC aU
You can imagine the last expression written in the functional form like a postfix expression of the form
postfix-expression ( expression-list )
where the postfix expression is
a.operator --( 0 ).operator ++( 0 ).operator ++().operator --().operator +
and the expression-list is
b.operator --( 0 )
In the C++ Standard (5.2.2 Function call) there is said that
8 [Note: The evaluations of the postfix expression and of the arguments
are all unsequenced relative to one another. All side effects of
argument evaluations are sequenced before the function is entered (see
1.9). —end note]
So it is implementation-defined whether at first the argument will be evaluated or the postfix expression. According to the showed output the compiler at first evaluates the argument and only then the postfix expression.
I would say they were wrong to include such a question.
Except as noted, the following excerpts are all from §[intro.execution] of N4618 (and I don't think any of this stuff has changed in more recent drafts).
Paragraph 16 has the basic definition of sequenced before, indeterminately sequenced, etc.
Paragraph 18 says:
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
In this case, you're (indirectly) calling some functions. The rules there are fairly simple as well:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. For each function invocation F, for every evaluation A that occurs within F and every evaluation B that does not occur within F but is evaluated on the same thread and as part of the same signal handler (if any), either A is
sequenced before B or B is sequenced before A.
Putting that into bullet points to more directly indicate order:
first evaluate the function arguments, and whatever designates the function being called.
Evaluate the body of the function itself.
Evaluate another (sub-)expression.
No interleaving is allowed unless something starts up a thread to allow something else to execute in parallel.
So, does any of this change before we're invoking the functions via operator overloads rather than directly? Paragraph 19 says "No":
The sequencing constraints on the execution of the called function (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.
§[expr]/2 also says:
Uses of overloaded operators are transformed into function calls as described
in 13.5. Overloaded operators obey the rules for syntax and evaluation order specified in Clause 5, but the requirements of operand type and value category are replaced by the rules for function call.
Individual operators
The only operator you've used that has somewhat unusual requirements with respect to sequencing are the post-increment and post-decrement. These say (§[expr.post.incr]/1:
The value computation of the ++ expression is sequenced before the modification of the operand object. With respect to an indeterminately-sequenced function call, the operation of postfix ++ is a single evaluation. [ Note: Therefore, a function call shall not intervene between the lvalue-to-rvalue conversion and the side effect associated with any single postfix ++ operator. —end note ]
In the end, however, this is pretty much just what you'd probably expect: if you pass x++ as a parameter to a function, the function receives the previous value of x, but if x is also in scope inside the function, x will have the incremented value by the time the body of the function starts to execute.
The + operator, however, does not specify ordering of the evaluation of its operands.
Summary
Using overloaded operators does not enforce any sequencing on the evaluation of sub-expressions within an expression, beyond the fact that evaluating an individual operator is a function call, and has the ordering requirements of any other function call.
More specifically, in this case, b-- is the operand to a function call, and --++a-- ++ is the expression that designates the function being called (or at least the object on which the function will be called--the -- designates the function within that object). As noted, ordering between these two is not specified (nor does operator + specify an order of evaluating its left vs. right operand).
There is not something in the C++ standard which says things need to be evaluated in this way. C++ has the concept of sequenced-before, where some operations are guaranteed to happen before other operations are. This is a partially-ordered set; that is, sosome operations are sequenced before others, two operations can’t be sequenced before eath other, and if a is sequenced before b, and b is sequenced before c, then a is sequenced before c. However, there are many types of operation which have no sequenced-before guarantees. Before C++11, there was instead a concept of a sequence point, which isn’t quite the same but similar.
Very few operators (only ,, &&, ?:, and ||, I believe) guarantee a sequence point between their arguments (and even then, until C++17, this guarantee doesn’t exist when the operators are overloaded). In particular, the addition does not guarantee any such thing. The compiler is free to evaluate the left-hand side first, to evaluate the right-hand side first, or (I think) even to evaluate them simultaneously.
Sometimes changing optimization options can change the results, or changing compilers. Apparently you aren’t seeing that; there are no guarantees here.
Operator precedence and associativity rules are only used to convert your expression from the original "operators in expression" notation to the equivalent "function call" format. After the conversion you end up with a bunch of nested function calls, which are processed in the usual way. In particular, order of parameter evaluation is unspecified, which means that there's no way to say which operand of the "binary +" call will get evaluated first.
Also, note that in your case binary + is implemented as a member function, which creates certain superficial asymmetry between its arguments: one argument is "regular" argument, another is this. Maybe some compilers "prefer" to evaluate "regular" arguments first, which is what leads to b-- being evaluated first in your tests (you might end up with different ordering from the same compiler if you implement your binary + as a freestanding function). Or maybe it doesn't matter at all.
Clang, for example, begins with evaluating the first operand, leaving b-- for later.
Take in account priority of operators in c++:
a++ a-- Suffix/postfix increment and decrement. Left-to-right
++a --a Prefix increment and decrement. Right-to-left
a+b a-b Addition and subtraction. Left-to-right
Keeping the list in your mind you can easily read the expression even without parentheses:
--++a--+++b--;//will follow with
--++a+++b--;//and so on
--++a+b--;
--++a+b;
--a+b;
a+b;
And dont forget about essential difference prefix and postfix operators in terms of order evaluation of variable and expression ))
I thought I understand how sequence points work in C++, but this GeeksQuiz question puzzled me:
int f(int &x, int c) {
c = c - 1;
if (c == 0) return 1;
x = x + 1;
return f(x, c) * x;
}
int main() {
int p = 5;
cout << f(p, p) << endl;
return 0;
}
The “correct” answer to this question says it prints 6561. Indeed, in VS2013 it does. But isn't it UB anyway because there is no guarantee which will be evaluated first: f(x, c) or x. We get 6561 if f(x, c) is evaluated first: the whole thing turns into five recursive calls: the first four (c = 5, 4, 3, 2) continue on, the last one (c = 1) terminates and returns 1, which amounts to 9 ** 4 in the end.
However, if x was evaluated first, then we'd get 6 * 7 * 8 * 9 * 1 instead. The funny thing is, in VS2013 even replacing f(x, c) * x with x * f(x, c) doesn't change the result. Not that it means anything.
According to the standard, is this UB or not? If not, why?
This is UB.
n4140 §1.9 [intro.execution]/15
Except where noted, evaluations of
operands of individual operators and of subexpressions of individual
expressions are unsequenced. [...] If a side effect on a scalar object
is unsequenced relative to [...] value computation using the value of
the same scalar object [...] the behavior is undefined.
Multiplicative operators don't have sequencing explicitly noted.
This is UB
Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression and the order of evaluation of the subexpressions within any expression) is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again.
There are exceptions to this rule which are noted below.
Except where noted below, there is no concept of left-to-right or
right-to-left evaluation in C++. This is not to be confused with
left-to-right and right-to-left associativity of operators: the
expression f1() + f2() + f3() is parsed as (f1() + f2()) + f3() due to
left-to-right associativity of operator+, but the function call to f3
may be evaluated first, last, or between f1() or f2() at run time.
This question already has answers here:
What is the correct answer for cout << a++ << a;?
(4 answers)
Closed 9 years ago.
I have the following code.
int x=80;
int &y=x;
x++;
cout<<x<<" "<<--y;
The output comes out to be 80 80. And I don't understand how. I thought the output of x would be 81 although I don't know anything about y. How is the reference variable affected by the decrement operator. Can someone please explain?
The expression is evaluated as:
((cout << x) << " ") << --y;
There is no sequence point (or ordering) between the evaluation of the left-hand side and the right-hand side of the expression, the compiler can output code that evaluates --y as the first step.
Since y is a reference to x here, this is actually undefined behavior since you're both reading from and writing to x in the same expression without intervening sequence points.
One nasty thing about redirection operators << is that they intuitively convey an indea of "sequential computation" that indeed is not present.
When you write
std::cout << f() << g() << std::endl;
the output will show first the result of f() and then the result of g(), but the actual call to g() may happen before the call to f().
It even gets worse than this... it's not that the sequence is not predictable, but that indeed the very concept of sequence is not valid. In
std::cout << f(g()) << h(i()) << std::endl;
it's for example legal that the first function being called is g(), followed by i(), followed by h() and finally by f(). It's not even guaranteed that order will be the same for all invocations (not because compiler makers likes to make fun of you, but because the code can be inlined and the compiler may decide a different order if the containing function is inlined in a different context).
The only C++ operators that guarantee a sequence in the evaluation order are:
&&: first evaluates left side and only if the result is "true" evaluates the right side
||: first evaluates left side and only if the result is "false" evaluates the right side
?:: evaluates first the condition and then only the second or the third operand
,: the comma operator... evaluates the left side, drops the value and then evaluates and returns the right side. NOTE: the commas between function parameters are NOT comma operators and no evaluation order is imposed.
Moreover this guaratee is valid only for the predefined operators. If you overload &&, || or , in your class they're just normal operators without any special restrictions on evaluation order.
Any other operator doesn't impose any restriction on the evaluation order, and this includes << even if the usage sort of tricks you into thinking that.
This is undefined behavior , C/C++ standards don't define the way argument are pushed into the stack, and usually argument are pushed in the reverse order, for example here:
func(1, 2)
will be evaluated to something like:
push 2
push 1
call func
so in your case, --y is evaluated and pushed before x does. It is clear in this example:
#include <iostream>
int a() { std::cout << "a" << std::endl ; return 1; }
int b() { std::cout << "b" << std::endl ; return 2; }
int main(void) {
std::cout << a() << " " << b() << std::endl;
return 0;
}
from the first look, it should print:
a
b
1 2
but it prints:
b
a
1 2
x++
increments x and produces as expression result the original value of x.
In particular, for x++ there is no time ordering implied for the increment and production of original value of x. The compiler is free to emit machine code that produces the original value of x, e.g. it might be present in some register, and that delays the increment until the end of the expression (next sequence point). Althought x++ seems to increment x to 81, it is not doing it until the print. As per the --y, it is getting the incremented value(81) and decrementing it before printing it as it is a prefix operator.
5.15 Logical OR operator in the standard says the following:
Unlike |, || guarantees left-to-right evaluation;
Does this mean somewhere I cannot locate in the standard, | is defined to evaluate right-to-left, or that it is implementation-defined? Does this vary when the operator is overloaded? I wrote a quick program to test this and both MSVC++ and GCC seem to evaluate right-to-left.
#include<iostream>
using namespace std;
int foo = 7;
class Bar {
public:
Bar& operator|(Bar& other) {
return *this;
}
Bar& operator++() {
foo += 2;
return *this;
}
Bar& operator--() {
foo *= 2;
return *this;
}
};
int main(int argc, char** argv) {
Bar a;
Bar b;
Bar c = ++a | --b;
cout << foo;
}
This outputs 16.
If ++a and --b are switched it outputs 19.
I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior), but I'm unsure how/if that applies with two separate instances as operands.
Ignore that operator for now, and just take note of this:
(x + y) * (z + 1)
Here, both operands must be evaluated before the multiplication can take place (otherwise we wouldn't know what to multiply). In C++, the order in which this is done is unspecified: it could be (x + y) first, or (z + 1) first, whatever the compiler feels is better.†
The same is true for the operator |. However, operator || must short-circuit, and in order to do that, it must evaluate strictly left to right. (And if the left evaluation yields true, the evaluation ends without evaluating the right operand.) That's what the sentence means.
†Note that it may have no preference one way or another, and just evaluate in the order it's listed. This is why you get the output you do, though you cannot rely on it at the language level.
As others said, it means that the order of the evaluation of the two sides is unspecified. To answer your other questions -
I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior)
No, your case does not modify foo in between two adjacent sequence points. Before entering a function and before leaving a function, there always is a sequence point, which means that both modifications of foo happen in between two different pairs of sequence points.
Does this vary when the operator is overloaded?
All of clause 5 only talks about builtin operators. For user defined operator implementations, the rules don't apply. So also for ||, for user defined operators the order is not specified. But notice that it is only for user defined operators; not when both operands are converted to bool and trigger the builtin operator:
struct A {
operator bool() const { return false; }
};
struct B {
operator bool() const { return true; }
};
int main() {
A a;
B b;
a || b;
shared_ptr<myclass> p = ...;
if(p && p->dosomething()) ...;
}
This will always first execute A::operator bool, and then B::operator bool. And it will only call p->dosomething() if p evaluates to true.
Does this mean somewhere I cannot locate in the standard, | is defined to evaluate right-to-left, or that it is implementation-defined?
Pedantically speaking the order of evaluation of arguments of | operator is unspecified. So that means the operands can be evaluated in either order.
However the order of evaluation of operands of logical operators (i.e &&, || etc) and comma operator is specified i.e from left to right.