When all does comma operator not act as a comma operator?
-
30-09-2019 - |
Question
If you see this code,
class A{
public:
A(int a):var(a){}
int var;
};
int f(A obj) {
return obj.var;
}
int main() {
//std::cout<<f(23); // output: 23
std::cout<<f(23, 23); // error: too many arguments to function 'int f(A)'
return 0;
}
f(23, 23)
does not compile because the comma acts as a separator here and not as a comma operator.
Where all does a comma not work as a comma operator? Or the other way around?
Solution
From a grammatical point of view, the parameters of a function call form an optional expression-list inside parentheses. An expression-list consists of one or more assignment-expression separated by a comma token. A comma can only signify a comma operator where an expression is expected.
The comma operator makes an expression out of an expression, a ,
and an assignment-expression, but an expression involving a comma operator is not itself an assignment-expression so can't appear in an expression-list except where it's a constituent of something that is an assignment-expression.
For example, you can surround any expression (including one using the comma operator) inside parentheses to from a primary-expression which is an assignment-expression and hence valid in an expression-list.
E.g.
postfix-expression where the expression-list consists of two assignment-expression each of which is an identifier.
f( a, b );
postfix-expression where the expression-list consists of a single assignment-expression which is a primary-expression which is a parenthesized expression using the comma operator.
f( (a, b) );
OTHER TIPS
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.
I did a search over the draft Standard. Basically, in the grammar the -list
productions are the ones that have commas in them to separate different items. The following results are C++03 specific. In C++0x, expression-list directly delegates to initializer-list because in C++0x brace lists can occur in function and constructor arguments likewise.
- expression-list For function/constructor arguments (including functional casts)
- enumerator-list The list of items of an enumeration
init-declarator-list The different names declared in one declaration
Example:
int a, b;
- parameter-declaration-list The parameter declaration list (surprise!) of a function
- initializer-list List similar to expression-list, but can include braced expression lists. Used for aggregate initialization (initializing arrays or structs)
member-declarator-list Similar to the init declarator list, but for member declarations in classes.
Example:
struct A { int a, b; };
- base-specifier-list List of base-classes of a class.
mem-initializer-list List of the initializers for members
Example:
struct A { A():a(0), b(0) { } int a; int b; };
- template-parameter-list List of template parameter declarations.
- template-argument-list List of template arguments passed to a template.
type-id-list List of types for exception specifications
Example:
void f() throw(int, bool) { }
There is an identifier-list for macro parameters too, which i haven't got in that list because it's really part of the preprocessor grammar.
This has to do with the language definition of expressions, which is quite complex.
f(1, 2)
is a function call expression with two parameters. Contrarily, f((1, 2))
is a function call expression with one parameter, which is the sub-expression 1, 2
, which will evaluate to 2.
A comma operator always acts as a comma operator, but a comma doesn't always signify a comma operator -- sometimes it's just punctuation.
As to when it's punctuation, the simple answer is "when the standard says so." Going through all the situations where the standard says so gives a much longer answer -- but one that's unlikely to be much more useful, because (for one example) it has to deal with a number of corner cases most people don't care much about.