C Comma Operator Assignment

Operator name Syntax Over​load​able Prototype examples (for class T)
Inside class definition Outside class definition
function call Yes R T::operator()(Arg1 &a1, Arg2 &a2, ... ...);N/A
comma Yes T2& T::operator,(T2 &b);T2& operator,(const T &a, T2 &b);
ternary conditional No N/AN/A

[edit]Explanation

The function call operator provides function semantics for any object.

The ternary conditional operator checks the boolean value of the first expression and, depending on the resulting value, evaluates and returns either the second or the third expression.

[edit]Built-in function call operator

The function call expressions have the form

where

  • E is an expression that names a function
  • A1, A2, A3,... is a possibly empty list of arbitrary expressions, except the comma operator is not allowed at the top level to avoid ambiguity.

The expression that names the function can be

a) lvalue expression that refers to a function

b) pointer to function

c) explicit class member access expression that selects a member function

d) implicit class member access expression, e.g. member function name used within another member function.

The function (or member) name specified by can be overloaded, overload resolution rules used to decide which overload is to be called.

If specifies a member function, it may be virtual, in which case the final overrider of that function will be called, using dynamic dispatch at runtime.

To call the function,

The expression as well as all expressions , , , etc, provided as arguments are evaluated in arbitrary order, unsequenced with respect to each other.

(until C++17)

The expression is sequenced before each of the expressions , , as well as default arguments, if any. The argument expressions are evaluated in arbitrary order, indeterminately sequenced with respect to each other.

(since C++17)

Each function parameter is initialized with its corresponding argument after implicit conversion if necessary. If the call is made to a member function, then the this pointer to current object is converted as if by explicit cast to the this pointer expected by the function. The initialization and destruction of each parameter occurs in the context of the caller, which means, for example, that if constructor of a parameter throws an exception, the exception handlers defined within the function, even as a function-try block, are not considered. If the function is a variadic function, default argument promotions are applied to all arguments matched by the ellipsis parameter. It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.

The return type of a function call expression is the return type of the chosen function, decided using static binding (ignoring the keyword), even if the overriding function that's actually called returns a different type. This allows the overriding functions to return pointers or references to classes that are derived from the return type returned by the base function, i.e. C++ supports covariant return types. If specifies a destructor, the return type is void.

When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object.

The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).

This allows objects of small class types, such as std::complex or , to be passed to or returned from functions in registers

(since C++17)

The value category of a function call expression is lvalue if the function returns an lvalue reference or an rvalue reference to function, is an xvalue if the function returns an rvalue reference to object, and is a prvalue otherwise. If the function call expression is a prvalue of object type, it must have complete type except when the prvalue is not materialized, such as(since C++17) when used as the operand of decltype (or as the right operand of a built-in comma operator expression that is the operand of ) .

Function call expression is similar in syntax to value initialization T(), to function-style cast expression T(A1), and to direct initialization of a temporary T(A1, A2, A3, ...), where is the name of a type.

Run this code

Output:

#include <cstdio>struct S {int f1(double d){return printf("%f \n", d);// variable argument function call}int f2(){return f1(7);// member function call, same as this->f1()// integer argument converted to double}};void f(){ puts("function called");// function call}int main(){ f();// function call S s; s.f2();// member function call}

[edit]Built-in comma operator

The comma operator expressions have the form

In a comma expression E1, E2, the expression 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 begins (note that a user-defined 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, . If is a temporary expression(since C++17), the result of the expression is that temporary expression(since C++17). If is a bit-field, the result is a bit-field.

The comma in various comma-separated lists, such as function argument lists (f(a, b, c)) and initializer lists int a[]={1,2,3}, is not the comma operator. If the comma operator needs to be used in such contexts, it has to be parenthesized: f(a, (n++, n+b), c)

Run this code

Output:

#include <iostream>int main(){int n =1;int m =(++n, std::cout<<"n = "<< n <<'\n', ++n, 2*n);std::cout<<"m = "<<(++m, m)<<'\n';}

[edit]Conditional operator

The conditional operator expressions have the form

The first operand of the conditional operator is evaluated and contextually converted to bool. After both the value evaluation and all side effects of the first operand are completed, if the result was true, the second operand is evaluated. If the result was false, the third operand is evaluated.

The type and value category of the conditional expression E1 ? E2 : E3 are determined according to the following rules:

1) If either or has type void, then one of the following must be true, or the program is ill-formed:

1.1) Either or (but not both) is a (possibly parenthesized) throw-expression. The result of the conditional operator has the type and the value category of the other expression. If the other expression is a bit field, the result is a bit field. Such conditional operator was commonly used in C++11 constexpr programming prior to C++14.
1.2) Both and are of type void (including the case when they are both throw-expressions). The result is a prvalue of type void.
2+2==4?throw123:throw456;

2) Otherwise, if or are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2.

(since C++14)

3) Otherwise, if and have different types, at least one of which is a (possibly cv-qualified) class type, or both are glvalues of the same value category and have the same type except for cv-qualification, then an attempt is made to form an implicit conversion sequence ignoring member access, whether an operand is a bit-field, or whether a conversion function is deleted(since C++14). from each of the operands to the target type determined by the other operand, as described below. An operand (call it ) of type can be converted the target type of the other operand (call it ) of type as follows:

3.1) If is an lvalue, the target type is , and the reference must bind directly to an lvalue;

3.2) If is an xvalue, the target type is , and the reference must bind directly;

3.3) If is a prvalue, or if neither the above conversion sequences can be formed and at least one of and is a (possibly cv-qualified) class type, the target type is the type that would have after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions

3.4) If both sequences can be formed (E2 to target type of E3 and E3 to target type of E2), or only one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.

3.5) If exactly one conversion sequence can be formed (note that it may still be ill-formed e.g. due to access violation), that conversion sequence is applied and the converted operand is used in place of the original operand for the remained of this description (starting at (4))

3.6) If no conversion sequence can be formed, the operands are left unchanged for the remainder of this description

4) If and are glvalues of the same type and the same value category, then the result has the same type and value category, and is a bit-field if at least one of and is a bit-field.

5) Otherwise, the result is a prvalue. If and do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is performed using the built-in candidates below to attempt to convert the operands to built-in types. If the overload resolution fails, the program is ill-formed. Otherwise, the selected conversions are applied and the converted operands are used in place of the original operands for step 6.

6) The lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are applied to the second and third operands. Then,

6.1) If both and now have the same type, the result is a prvalue of that type designating a temporary object(until C++17)whose result object is(since C++17) copy-initialized from whatever operand was selected after evaluating .

6.2) If both and have arithmetic or enumeration type: the usual arithmetic conversions are applied to bring them to common type, and that type is the result.

6.3) If both and are pointers, or one is a pointer and the other is a null pointer constant, then pointer conversions and qualification conversions are applied to bring them to common type, and that type is the result.

6.4) If both and are pointers to members, or one is a pointer to member and the other is a null pointer constant, then pointer-to-member conversions and qualification conversions are applied to bring them to common type, and that type is the result.

6.5) If both and are null pointer constants, and at least one of which is of type std::nullptr_t, then the result's type is .

6.6) In all other cases, the program is ill-formed.

For every pair of promoted arithmetic types L and R and for every type P, where P is a pointer, pointer-to-member, or scoped enumeration type, the following function signatures participate in the overload resolution performed in step 5 of the rules above:

LR operator?:(bool, L, R );

P operator?:(bool, P, P );

where LR is the result of usual arithmetic conversions performed on and . The operator “?:” cannot be overloaded, these function signatures only exist for the purpose of overload resolution.

The return type of a conditional operator is also accessible as the binary type trait std::common_type.

Run this code

Output:

#include <string>#include <iostream>struct Node { Node* next;int data;// deep-copying copy constructor Node(const Node& other): next(other.next? new Node(*other.next):NULL) , data(other.data){} Node(int d): next(NULL), data(d){} ~Node(){ delete next ;}};int main(){// simple rvalue exampleint n =1>2?10:11;// 1>2 is false, so n = 11// simple lvalue exampleint m =10;(n == m ? n : m)=7;// n == m is false, so m = 7std::cout<<"n = "<< n <<"\nm = "<< m;//output the result}

[edit]Standard library

Many classes in the standard library overload to be used as function objects.

operator()

deletes the object or array
(public member function of )[edit]

operator()

returns the sum of two arguments
(public member function of )[edit]

operator()

returns the difference between two arguments
(public member function of )[edit]

operator()

returns the product of two arguments
(public member function of )[edit]

operator()

returns the result of the division of the first argument by the second argument
(public member function of )[edit]

operator()

returns the remainder from the division of the first argument by the second argument
(public member function of )[edit]

operator()

returns the negation of the argument
(public member function of )[edit]

operator()

checks if the arguments are equal
(public member function of )[edit]

operator()

checks if the arguments are not equal
(public member function of )[edit]

operator()

checks if the first argument is greater than the second
(public member function of )[edit]

operator()

checks if the first argument is less than the second
(public member function of )[edit]

operator()

checks if the first argument is greater than or equal to the second
(public member function of )[edit]

operator()

checks if the first argument is less than or equal to the second
(public member function of )[edit]

operator()

returns the logical AND of the two arguments
(public member function of )[edit]

operator()

returns the logical OR of the two arguments
(public member function of )[edit]

operator()

returns the logical NOT of the argument
(public member function of )[edit]

operator()

returns the result of bitwise AND of two arguments
(public member function of )[edit]

operator()

returns the result of bitwise OR of two arguments
(public member function of )[edit]

operator()

returns the result of bitwise XOR of two arguments
(public member function of )[edit]

operator()

returns the logical complement of the result of a call to the stored predicate
(public member function of )[edit]

operator()

returns the logical complement of the result of a call to the stored predicate
(public member function of )[edit]

operator()

calls the stored function
(public member function of )[edit]

operator()

invokes the target
(public member function of )[edit]

operator()

lexicographically compares two strings using this locale's collate facet
(public member function of )[edit]

operator()

compares two values of type
(public member function of )[edit]

operator()

compares two values of type
(public member function of )[edit]

operator()

executes the function
(public member function of )[edit]

operator()

advances the engine's state and returns the generated value
(public member function of )[edit]

operator()

generates the next random number in the distribution
(public member function of )[edit]

The comma operator is not overloaded by any class in the standard library. The boost library uses in boost.assign, boost.spirit, and other libraries. The database access library SOCI also overloads .

[edit]Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 1550 C++98 parenthesized throw-expression not allowed in ?: if other operand is non-void parenthesized throw-expressions accepted
CWG 1560 C++98 void operand in ?: caused gratuitous l-to-r conversion on the other operand,
always resulting in rvalue
 ?:with a void can be lvalue
CWG 1932 C++14 same-type bit fields were missing in ?: handled by underlying types
CWG 1895 C++14 unclear if deleted or inaccessible conversion function prevents conversion in ?:,
and conversions from base class to derived class prvalue were not considered
handled like overload resolution

[edit]See also

Operator precedence

Operator overloading

Common operators
assignment increment
decrement
arithmetic logical comparison member
access
other

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[b]
*a
&a
a->b
a.b
a->*b
a.*b

a(...)
a, b
?:

Special operators

converts one type to another related type
converts within inheritance hierarchies
adds or removes cv qualifiers
converts type to unrelated type
C-style cast converts one type to another by a mix of , , and
creates objects with dynamic storage duration
destructs objects previously created by the new expression and releases obtained memory area
queries the size of a type
queries the size of a parameter pack(since C++11)
queries the type information of a type
checks if an expression can throw an exception (since C++11)
queries alignment requirements of a type (since C++11)

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).

The use of the comma token as an operator is distinct from its use in function calls and definitions, variable declarations, enum declarations, and similar constructs, where it acts as a separator.

Syntax[edit]

The comma operator separates expressions (which have value) in a way analogous to how the semicolon terminates statements, and sequences of expressions are enclosed in parentheses analogously to how sequences of statements are enclosed in braces: is a sequence of expressions, separated by commas, which evaluates to the last expression while is a sequence of statements, and does not evaluate to any value. A comma can only occur between two expressions – commas separate expressions – unlike the semicolon, which occurs at the end of a (non-block) statement – semicolons terminate statements.

The comma operator has the lowest precedence of any C operator, and acts as a sequence point. In a combination of commas and semicolons, semicolons have lower precedence than commas, as semicolons separate statements but commas occur within statements, which accords with their use as ordinary punctuation: is grouped as because these are two separate statements.

Examples[edit]

In this example, the differing behavior between the second and third lines is due to the comma operator having lower precedence than assignment. The last example differs as well since the return expression must be fully evaluated before the function can return.

/** * Commas act as separators in this line, not as an operator. * Results: a=1, b=2, c=3, i=0 */inta=1,b=2,c=3,i=0;/** * Assigns value of b into i. * Results: a=1, b=2, c=3, i=2 */inta=1,b=2,c=3;inti=(a,b);/** * Assigns value of a into i. Equivalent to (i = a), b; * Results: a=1, b=2, c=3, i=1 * (The curly braces on the second line are needed to * avoid a compiler error. The second 'b' declared * is given no initial value.) */inta=1,b=2,c=3;{inti=a,b;}/** * Increases value of a by 2, then assigns value of resulting operation a+b into i . * Results: a=3, b=2, c=3, i=5 */inta=1,b=2,c=3;inti=(a+=2,a+b);/** * Increases value of a by 2, then stores value of a to i, and discards unused * values of resulting operation a + b . Equivalent to (i = (a += 2)), a + b; * Results: a=3, b=2, c=3, i=3 */inta=1,b=2,c=3;inti;i=a+=2,a+b;/** * Assigns value of a into i; the following 'b' and 'c' * are not part of the initializer but declarators for * second instances of those variables. * Results: a=1, b=2, c=3, i=1 * (The curly braces on the second line are needed to * avoid a compiler error. The second 'b' and second * 'c' declared are given no initial value.) */inta=1,b=2,c=3;{inti=a,b,c;}/** * Assigns value of c into i, discarding the unused a and b values. * Results: a=1, b=2, c=3, i=3 */inta=1,b=2,c=3;inti=(a,b,c);/** * Returns 6, not 4, since comma operator sequence points following the keyword * 'return' are considered a single expression evaluating to rvalue of final * subexpression c=6 . */returna=4,b=5,c=6;/** * Returns 3, not 1, for same reason as previous example. */return1,2,3;/** * Returns 3, not 1, still for same reason as above. This example works as it does * because return is a keyword, not a function call. Even though compilers will * allow for the construct return(value), the parentheses are only relative to "value" * and have no special effect on the return keyword. * Return simply gets an expression and here the expression is "(1), 2, 3". */return(1),2,3;

Uses[edit]

The comma operator has relatively limited use cases. Because it discards its first operand, it is generally only useful where the first operand has desirable side effects. Further, because it is rarely used outside of specific idioms, and easily mistaken with other commas or the semicolon, it is potentially confusing and error-prone. Nevertheless, there are certain circumstances where it is commonly used, notably in for loops and in SFINAE.[1] For embedded systems which may have limited debugging capabilities, the comma operator can be used in combination with a macro to seamlessly override a function call, to insert code just before the function call.

For loops[edit]

The most common use is to allow multiple assignment statements without using a block statement, primarily in the initialization and the increment expressions of a for loop. This is the only idiomatic use in elementary C programming. In the following example, the order of the loop's initializers is significant:

voidrev(char*s,size_tlen){char*first;for(first=s,s+=len;s>first;){--s;putchar(*s);}}

An alternative solution to this problem is parallel assignment, which allows multiple assignments to occur within a single statement, and also uses a comma, though with different syntax and semantics. This is used in Go in its analogous for loop.[2]

Outside of for loop initializers (which have a special use of semicolons), the comma might be used synonymously with the semicolon, particularly when the statements in question function similarly to a loop increment (e.g. at the end of a while loop):

However, as this usage achieves the same thing as the semicolon in a visually different way, this is of dubious usefulness and might confuse readers.

Macros[edit]

The comma can be used in preprocessor macros to perform multiple operations in the space of a single syntactic expression.

One common use is to provide custom error messages in failed assertions. This is done by passing a parenthesized expression list to the macro, where the first expression is an error string and the second expression is the condition being asserted. The macro outputs its argument verbatim on an assertion failure. The following is an example:

#include<stdio.h>#include<assert.h>intmain(void){inti;for(i=0;i<=9;i++){assert(("i is too big!",i<=4));printf("i = %i\n",i);}return0;}

Output:

i = 0 i = 1 i = 2 i = 3 i = 4 assert: assert.c:6: test_assert: Assertion `( "i is too big!", i <= 4 )' failed. Aborted

However the assert macro is usually disabled in production code, so use it only for debug purposes.

Condition[edit]

The comma can be used within a condition (of an if, while, do while, or for) to allow auxiliary computations, particularly calling a function and using the result, with block scoping:

if(y=f(x),y>x){...// statements involving x and y}

A similar idiom exists in Go, where the syntax of the if statement explicitly allows an optional statement.[3]

Complex return[edit]

The comma can be used in return statements, to assign to a global variable or out parameter (passed by reference). This idiom suggests that the assignments are part of the return, rather than auxiliary assignments in a block that terminates with the actual return. For example, in setting a global error number:

if(failure)return(errno=EINVAL,-1);

This can be written more verbosely as:

if(failure){errno=EINVAL;return-1;}

Avoid a block[edit]

For brevity, the comma can be used to avoid a block and associated braces, as in:

if(x==1)y=2,z=3;if(x==1)y=2,z=3;

instead of:

if(x==1){y=2;z=3;}if(x==1){y=2;z=3;}

Other languages[edit]

In the OCaml and Ruby programming languages, the semicolon (";") is used for this purpose. JavaScript and Perl utilize the comma operator in the same way C/C++ does.

See also[edit]

References[edit]

  • Ramajaran, V. (1994), Computer Programming in C, New Delhi: Prentice Hall of India 
  • Dixit, J.B (2005), Fundamentals of computers and programming in C, New Delhi: Laxmi Publications 
  • Kernighan, Brian W.; Ritchie, Dennis M. (1988), The C Programming Language (2nd ed.), Englewood Cliffs, NJ: Prentice Hall 

External links[edit]

0 thoughts on “C Comma Operator Assignment

Leave a Reply

Your email address will not be published. Required fields are marked *