I recently looked at my old code and I can't figure out what this does, or whether it is valid. The code is something like:
map<string, string> map;
map[string1] = ("s", string2);
It's an obfuscation. The comma operator evaluates all the arguments from left to right but discards all arguments apart from the final one. The formal type of the entire expression is the type of the final argument.
(Note also that the comma itself is a sequencing point.)
string2 becomes the value in the map, under key string1.
The expression "s", string2 uses the built-in1 comma operator. It has the following semantics:
In a comma expression E1, E2, the expression E1 is evaluated, its result is discarded, and its side effects are completed before evaluation of the expression E2 begins (note that a user-defined operator, cannot guarantee sequencing) (until C++17).
The type, value, and value category of the result of the comma expression are exactly the type, value, and value category of the second operand, E2.
In case of your code snippet, it isn't useful at all, and its only purpose is to confuse readers. Since evaluating "s" has no side-effects, the code is identical to this:
map<string, string> map;
map[string1] = string2;
1 This is assuming that no user-defined operator, matching the arguments has been defined. If there is, it can do anything, and the remainder of this answer does not apply. You'd have to look into the source code to find out, what the expression evaluates to.
This is probably the comma operator, which evaluates everything then discards everything except the right hand side value.
But, if operator,( char const*, string2 ) is overloaded it could do anything (where string2 is the type of string2).
Look for such overloads.
Note that if string2 is a std::string such overloads may make your program ill formed, no diagostic required. Won't stop people from doing it. And it may "work" and still permit , to (for example) cause concatination of strings.
Comma is operator, that evaluates arguments from left to right, and has value of most-right element.
For example:
int a=2, b=3, c;
c = (a+=b, a*b);
So, from left, a+=b is evaulated first, which sets a to 2+3, then a*b is evaulated, and expression has value of 5*3, which value is used. So c is 15
Related
While I didn't know you cannot overload operator[] in C++ to accept more than one argument, I have accidentally came across the statement that appeared to be valid to my surprise:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers{1, 2, 3, 4};
int i = 0;
std::cout << numbers[i++,i+=1,i=1,i+1] << std::endl;
return 0;
}
So could anyone please explain if there is any benefit of passing multiple expressions to operator[] ?
compiled with mingw g++ 4.8.1 with -std=c++11
You are not passing multiple arguments to an overloaded operator, but instead you use the comma operator for evaluating a single function parameter. There is no benefit associated with it, except confusing co-workers you dislike. The statement
numbers[i++,i+=1,i=1,i+1]
evaluates i++, then i += 1, then i = 1, then i + 1 and returns the last evaluated expression, which is 2.
While there are valid use cases for the comma operator, this is not one of them.
In C++, comma works both as a separator and an operator. In this particular case, it works as an operator.
In a comma expression E1, E2, the expression E1 is evaluated, its result is discarded (although if it has class type, it won't be destroyed until the end of the containing full expression), and its side effects are completed before evaluation of the expression E2 begins (note that a user-defined operator, cannot guarantee sequencing) (until C++17).
The type, value, and value category of the result of the comma expression are exactly the type, value, and value category of the second operand, E2. If E2 is a temporary expression (since C++17), the result of the expression is that temporary expression (since C++17). If E2 is a bit-field, the result is a bit-field.
So
numbers[i++,i+=1,i=1,i+1];
becomes
numbers[i+1];
Apart from possible side effects, there isn't any benefit, as your numbers[i++, i+=1, i=1, i+1] expression evaluates to numbers[i+1] which is equal to numbers[2] because of how things work when using the comma operator. We can argue this is confusing, hard to read and is of no benefit.
This is something that might work in interpreted languages like Python, but not in C++.
In this case you have to chain them with the numbers in front
...<<numbers[i-1]<<number[i]<<...
Try to base your design around this limitation.
This question already has answers here:
What does the comma operator , do?
(8 answers)
C++ -- return x,y; What is the point?
(18 answers)
Closed 6 years ago.
I understand what this C++ function does, but I don't understand why the return statement is written this way:
int intDivide(int num, int denom){
return assert(denom!=0), num/denom;
}
There is only one statement here, because there is only one ; but the comma confuses me. Why not write:
int intDivide(int num, int denom){
assert(denom!=0);
return num/denom;
}
Aside from "elegance" is there something to be gained in the first version?
What exactly is that comma doing anyway? Does it break a single statement into 2 parts such that essentially the above 2 versions are identical?
Although the code didn't seem to use constexpr, C++11 constexpr functions were constrained to have only one statement which had to be a return statement. To do the non-functional assertion and return a value there would be no other option than using the comma operator. With C++14 this constraint was removed, though.
I could imagine that the function was rewritten from a macro which originally read something like this
#define INT_DIVIDE(nom,denom) (assert(denom != 0), nom/denom)
The built-in comma operator simply sequences two expressions. The result of the expression is the second operand. The two functions are, indeed, equivalent. Note, that the comma operator can be overloaded. If it is, the expressions are not sequenced and the result is whatever the overload defines.
In practice the comma operator sometimes comes in quite handy. For example, it is quite common to use the comma operator when expanding a parameter pack: in some uses each of the expansions is required to produce a value and to avoid void results messing things up, the comma operator can be used to have a value. For example:
template <typename... T>
void g(T const& arg) {
std::initializer_list<bool>{ (f(arg), true)... };
}
This is a sort of 'syntactic sugar', which is expanded on in a similar question.
Basically the e1, e2 means evaluate e1, and then evaluate e2 - and the entire statement is the result of e2. It's a short and obfuscated (in my opinion) way of writing what you suggest. Maybe the writer is cheap on code lines.
From the C++ standard:
5.19 Comma operator [expr.comma]
1 The comma operator groups left-to-right.
expression:
assignment-expression
expression , assignment-expression
A pair of expressions separated by a comma is
evaluated left-to-right; the left expression is a discarded- value
expression (Clause 5).87 Every value computation and side effect
associated with the left expression is sequenced before every value
computation and side effect associated with the right expression. The
type and value of the result are the type and value of the right
operand; the result is of the same value category as its right
operand, and is a bit-field if its right operand is a glvalue and a
bit-field. If the value of the right operand is a temporary (12.2),
the result is that temporary.
Yes, the two versions are identical, except if the comma operator is overloaded, as #StoryTeller commented.
In the for loop at initialization part you can declare and initialize many variables as you like but ofcourse they have to be same type.In conditional part you can apply any expressional statements like AND(&&),OR(||),>,<,== etc.
but(,) is not a expression .How it works here
just a=1,2,3,4,5,6 and b=1,2,3,4,5,6,7,8,9,10
and a<6,b<9 returns a=1,2,3,4,5,6,7,8,9=b
for(int a=1,b=1,c=2,d=5;a<4,b<10;a++,b++)//initialize variables and using , between expression
{
cout<<a<<" "<<b<<endl;
}
Because...that's not really how things work at all.
The comma operator evaluates and discards its left operand (so in most cases its left operand will have side effects). After the left operand is evaluated (and any side effects from it have happened), the right operand is evaluate. The value yielded from this is the value of the right operand.
Actually it's not or, the behavior of comma operator can be described as:
In the C and C++ programming languages, the comma operator
(represented by the token ,) is a binary operator that evaluates its
first operand and discards the result, and then evaluates the second
operand and returns this value (and type).
From wiki: https://en.wikipedia.org/wiki/Comma_operator
So only the result of k<10 taken into the account.
Why the compiler (g++-4.9) is not complaining on this notation?
double d=(4,5,6);
and if I debug it, the value of d is 6?
What do the round brackets mean in this expression?
P.S.
I've enabled C++11
This uses the comma operator, which (without overloading) just evaluates the left-hand expression, throws away the result, and returns the result of the right-hand expression.
Since the expressions 4 and 5 have no side-effects, your code is equivalent to:
double d = 6;
This is comma operator.
In a comma expression E1, E2, the expression E1 is evaluated, its
result is discarded, and its side effects are completed before
evaluation of the expression E2 begins (note that a user-defined
operator, cannot guarantee sequencing).
For (4,5,6), first evaluate expression 4, disregard its return value and complete any side-effects (nothing here indeed), then do the same thing with 5, then evaluate the last expression 6, returning the type and the result of this evaluation.
For example for such statement:
c += 2, c -= 1
Is it true that c += 2 will be always evaluated first, and c in second expression c-= 1 will always be updated value from expression c += 2?
Yes, it is guaranteed by the standard, as long as that comma is a non-overloaded comma operator. Quoting n3290 ยง5.18:
The comma operator groups left-to-right.
expression:
assignment-expression
expression , assignment-expression
A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-
value expression (Clause 5)83. Every value computation and side effect associated with the left expression
is sequenced before every value computation and side effect associated with the right expression. The type
and value of the result are the type and value of the right operand; the result is of the same value category
as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field.
And the corresponding footnote:
83 However, an invocation of an overloaded comma operator is an ordinary function call; hence, the evaluations of its argument
expressions are unsequenced relative to one another (see 1.9).
So this holds only for the non-overloaded comma operator.
The , between arguments to a function are not comma operators. This rule does not apply there either.
For C++03, the situation is similar:
The comma operator groups left-to-right.
expression:
assignment-expression
expression , assignment-expression
A pair of expressions separated by a comma is evaluated left-to-right and the value of the left expression is
discarded. The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conver-
sions are not applied to the left expression. All side effects (1.9) of the left expression, except for the
destruction of temporaries (12.2), are performed before the evaluation of the right expression. The type and
value of the result are the type and value of the right operand; the result is an lvalue if its right operand is.
Restrictions are the same though: does not apply to overloaded comma operators, or function argument lists.
Yes, the comma operator guarantees that the statements are evaluated in left-to-right order, and the returned value is the evaluated rightmost statement.
Be aware, however, that the comma in some contexts is not the comma operator. For example, the above is not guaranteed for function argument lists.
Yes, in C++ the comma operator is a sequence point and those expression will be evaluated in the order they are written. See 5.18 in the current working draft:
[snip] is evaluated left-to-right. [snip]
I feel that your question is lacking some explanation as to what you mean by "side effects". Every statement in C++ is allowed to have a side effect and so is an overloaded comma operator.
Why is the statement you have written not valid in a function call?
It's all about sequence points. In C++ and C it is forbidden to modify a value twice inside between two sequence points. If your example truly uses operator, every self-assignment is inside its own sequence point. If you use it like this foo(c += 2, c -= 2) the order of evaluation is undefined. I'm actually unsure if the second case is undefined behaviour as I do not know if an argument list is one or many sequence points. I ought to ask a question about this.
It should be always evaluated from left to right, as this is the in the definition of the comma operator:
Link
You've got two questions.
The first question: "Is comma operator free from side effect?"
The answer to this is no. The comma operator naturally facilitates writing expressions with side effects, and deliberately writing expressions with side effects is what the operator is commonly used for. E.g., in while (cin >> str, str != "exit") the state of the input stream is changed, which is an intentional side effect.
But maybe you don't mean side-effect in the computer science sense, but in some ad hoc sense.
Your second question: "For example for such statement: c += 2, c -= 1 Is it true that c += 2 will be always evaluated first, and c in second expression c-= 1 will always be updated value from expression c += 2?"
The answer to this is yes in the case of a statement or expression, except when the comma operator is overloaded (very unusual). However, sequences like c += 2, c -= 1 can also occur in argument lists, in which case, what you've got is not an expression, and the comma is not a sequence operator, and the order of evaluation is not defined. In foo(c += 2, c -= 1) the comma is not a comma operator, but in foo((c += 2, c -= 1)) it is, so it may pay to pay attention to the parentheses in function calls.