Which comes first?

Pages: 123
Can I write?
a = (b = 5, c = 10);
And if it's possible, please, calculate this expression... I have no idea what a comma between expressions and expressions is.
Last edited on
The comma operator causes things to be evaluated in sequence and returns the value of the last expression.

Hence:
b = 5;
c = 10;
a = c;

As a matter of style, don't do that.
comma calculates the left side (b=5),then calculates the right side (c=10),then returns the result of ther right side (reference to c).
There are several online compilers available if you want to test if something compiles or what it evaluates to: http://ideone.com , http://liveworkspace.org and others.
Last edited on
Lol the lack necessity of some features of C++. I do like lambdas though but it makes sense if you think about general architectures.

if b is a register
and c is a register
then a is the source
b = c
a = b = c
therefore a = c
Last edited on
Cubbi wrote:
comma calculates the left side (b=5),then calculates the right side (c=10)


I'm nearly certain the order is undefined.

Of course I could be wrong. I know it's undefined for most binary operators. I thought the only exception was for || and && to allow for short circuiting.
Last edited on
The order is undefined for user-defined operator,(). The built-in comma operator is left-to-right.
so then &&, ||, and the comma have their order defined, but nothing else does?

That seems strange to me. Why make an exception for the comma?
@Disch
What other binary operators don't have their order defined?

I just looked at the operators page on this site and it's recently, to me anyways, been updated. http://www.cplusplus.com/doc/tutorial/operators/

It covers a lot of information, including the , operator which I've never seen before, not on there anyways.
It is important to append that after the comma there is a sequence point. So if you will write expression

1
2
3
4
5
int b = 5, c = 10;

int a = ( b++, c++, b + c );

std::cout << "a = " << a << std::endl;

a will get value 17.

Or another example

1
2
3
4
5
static int b;

int a = ( b++, b++, b++ );

std::cout << "a = " << a << std::endl;

b will be equal to 3 and a will be equal to 2.

The last example can be rewritten that to make it more interesting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int f()
{
   static int b;

   return ( b++, b++, b++ );
}

int main()
{
   int a = ( f(), f() );

   std::cout << "a = " << a << std::endl;
}


a will be equal to 5. :)
Or even it can be written the following way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

int main()
{
	struct B
	{
		int operator ()() const
		{
			static int b;
			return ( b++, b++, b++ );
		}
	} b;

	int a = ( b(), b() );

	std::cout << "a = " << a << std::endl;
}


Enjoy!
Last edited on
@Vlad:

I understand the implications. I just didn't realize comma was defined that way when pretty much everything else isn't. It's just a little surprising to me... but I guess it kind of makes sense.

VolatilePulse wrote:
@Disch
What other binary operators don't have their order defined?


Most of them:
+ - * / | & ^ % << >> = <= >= += -= *= /= %= <<= >>= != == |= &= ^=


To continue with vlad's example:

1
2
3
4
5
6
7
8
9
10
11
int a = 1;

// this results in undefined behavior because the order in which
//  'a' and '++a' are evaluated is undefined:

int b = (++a + a);

// this, however, is apparently perfectly OK and is defined,
//  which is what surprised me:

int c = (++a, a);




EDIT:

To add to the confusion... the comma operator apparently ISN'T defined when it's used in a parameter list.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int junk(int a, int b)
{
    cout << a << " " << b << "\n";
    return 5;
}

int main()
{
    int v = 1;

    junk( ++v, ++v );  // prints "3 3" on my system, illustrating undefined behavior

    cin.get();
}


So the comma operator used to separate arguments must not be the same comma operator used to separate other expressions. Which makes it even more confusing why they made the special case for the comma operator....
Last edited on
I understand it might not be defined by the standard, but logically, that makes perfect sense. The prefix increment operator is evaluated before the value of v is. Once both operators are evaluated, the variable can be evaluated for the call stack (right? the call stack?).

Same goes for (++a + a). increment operators take precedence over mathematical operators so we evaluate the increment operator, then the mathematical operator. The only time I could see some confusion would be the behavior of the postfix versions.

I also believe most of the operators have defined behavior, however, they're not meant to be linked together. You try hard enough to break something, it'll eventually give. The order of precedence plays a huge role, however, I don't know what they are, and when mixed with operators of a different class is when undefined behavior comes into play.
> So the comma operator used to separate arguments ...

There, the comma is not an operator, it is a separator. Comma is an operator when used in an expression.

Try this:

1
2
3
4
5
6
7
8
9
10
11
12
#include <cstring>

int main()
{
    const char srce[] = "hello" ;
    char dest[ sizeof(srce) ] ;

    std::strcpy( dest, srce ) ; // fine, the comma here is a seperator
  
    std::strcpy( ( dest, srce ) ) ; // ***error, the comma is an operator
    // ( dest, srce ) is an expression 
}
Last edited on
Volatile Pulse wrote:
I also believe most of the operators have defined behavior, however, they're not meant to be linked together. You try hard enough to break something, it'll eventually give.


The operators have clearly defined behavior. And the order of precedence is clearly defined. What isn't defined is the order in which expressions on either side of an operator are evaluated before the operator itself is evaluated.

Example:

 
a + b * c;


The following are clearly defined by the language:

- 'b' will be evaluated before '*'
- 'c' will be evaluated before '*'
- 'a' will be evaluated before '+'
- '*' will be evaluated before '+'

However the order of anything outside of that is undefined. The compiler may evaluate in any of the below orders:

b, c, *, a, +
c, b, a, *, +
a, c, b, *, +
c, b, *, a, +

etc


JLBorges wrote:

There, the comma is not an operator, it is a separator


Fair enough... I guess my terminology was incorrect. Still is confusing/questionable though.

Anyway I understand, so I don't know if we need to drag this out more. I just thought it was strange, is all.
@vlad
It's simple logic. That's not really that confusing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
int b = 5, c = 10;

int a = ( b++, c++, b + c );

std::cout << "a = " << a << std::endl;

// Evaluates to

int b = 5;
int c = 10;

b ++; // 6
c ++; // 11
int a = b + c; // 17

std::cout << "a = " << a << std::endl; // a = 17
////////////////////////////////////////////////////////////////////////////////
static int b;

int a = ( b++, b++, b++ );

std::cout << "a = " << a << std::endl;

// Evaluates to

static int b; // Not sure why you use static, but should be initialized to 0
// Assuming ints are initialized to 0
b ++; // 1
b ++; // 2
int a = b++; // a = 2 | b = 3

std::cout << "a = " << a << std::endl; // a = 2
////////////////////////////////////////////////////////////////////////////////
int f()
{
   static int b;

   return ( b++, b++, b++ );
}

int main()
{
   int a = ( f(), f() );

   std::cout << "a = " << a << std::endl;
}

// Evaluates to
// 1st call to f
static int b; // Again assuming implementation of ints is 0
// b = 0;
b ++; // 1
b ++; // 2
return b ++; // 2 is returned | b = 3
// 2nd call to f
b; // 3
b ++; // 4
b ++; // 5
return b ++; // 5 is returned | b = 6
// Returning to main
int a = 5; // a is assigned the last expression in the comma operator

std::cout << "a = " << a << std::endl; // a = 5;
////////////////////////////////////////////////////////////////////////////////
int main()
{
	struct B
	{
		int operator ()() const
		{
			static int b;
			return ( b++, b++, b++ );
		}
	} b;

	int a = ( b(), b() );

	std::cout << "a = " << a << std::endl;
}
// Evaluates to essentially the same as above
std::cout << "a = " << a << std::endl; // a = 5 


@JLBorges
The comma is only a separator in arguments, nothing else. In all other situations, it's an operator.

Edit: You're introducing the () operator in your second call, which is evaluated first, which in turns only passes one argument.

@Disch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int junk(int a, int b)
{
    cout << a << " " << b << "\n";
    return 5;
}

int main()
{
    int v = 1;

    junk( ++v, ++v );  // prints "3 3" on my system, illustrating undefined behavior

    cin.get();
}
// Evaluates to
int v = 1; // 1
++ v; // 2
++ v; // 3
junk (3, 3);
cout << a << " " << b << "\n"; // 3 3 
Last edited on
@Disch
Here is your example with a bit of a twist on it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int a = 1, b = 2, c = 3;

++ a + ++ b * c ++;

// Evaluates to

// - 'b' will be evaluated before '*'
// - 'c' will be evaluated before '*'
// - 'a' will be evaluated before '+'
// - '*' will be evaluated before '+'

// Following the above logic, the equation can be rewritten like so

(++ a) + ((++ b) * (c ++));
(++ a) + ((3) * (c ++)); // prefix increment, b is increased and returned | 3
(++ a) + ((3) * (3)); // postfix increment, c is returned but then increased | 4
(2) + ((3) * (3)); // prefix increment, a is increased and returned | 2
(2) + (9); // multiplication first
(11); // addition 


Keeping it reasonable to factor out, there isn't anything you guys can give me that doesn't logically make sense. I believe the only reason it isn't defined is because it's probably hard for tell a computer how to do all of the stuff (on large scales) or just to keep people from being dumb, so when they expect something different, it can be blamed on implementation defined and not standard defined.

Try running the above example and see if you get 11 as well.

I'm in the mood for challenges, so I'd like to see what you guys can come up with.
> The comma is only a separator in arguments, nothing else. In all other situations, it's an operator.

The comma is an operator only when used as part of an expression, and nowhere else.
In all other situations, it's a separator.

1
2
3
4
5
6
7
const int i = 7, j = 22 ;

template < typename T = int, int N = 10 > struct foo ;

int a[] = { 1, 2, 3, 4 } ;

struct A { int x, y ; } aa  = { 1, 2 }, bb = { 3, 4 } ;


Operators are there only in expressions; for example in the code above, = is not an operator.
Last edited on
@Volatile Pulse
The result of ++ a + ++ b * c ++; in your latest example is well defined. It will always give you 11. It doesn't matter in what order the sub-expressions are evaluated.
Last edited on
JLBorges wrote:
The comma is an operator only when used as part of an expression, and nowhere else.
In all other situations, it's a separator.


In all but the first example, I'd definitely consider them arguments. Arguments to a template, arguments to an array initialization, arguments to an object initialization. The first example is still an expression to me, each piece gets evaluated separately. The only difference is that you're declaring something. If you use the comma operator during declaration, it separates the expression, and makes each part of the expression declared as the data type you declared the first one as. Try this for example:

for (auto i = 1, it = myVec.begin();...) // *ERROR*

Refer to above response

@Peter87
From the way Disch explained it, it sounded like no possible way it could be defined to always be true. Is there a variation of that that isn't defined?
> I'd definitely consider them arguments
> Arguments to a template, arguments to an array initialization, arguments to an object initialization.

It is a free world. You can consider anything as anything you fancy.
The problem is when you use the same variable more than once in the same expression and the variable is modified at least once. This is the case with ++a + a. if a is 1 and ++a is evaluated before a you get 2 + 2. If instead a is evaluated before ++a you get 2 + 1. The standard says the result is undefined so you could get something completely different, but I hope this helps understanding the problem.
Last edited on
Pages: 123