8

From this former question When all does comma operator not act as a comma operator?, I understood that commas inside a function call can only act as expression sperator. But from the code below, it appears that operator() behaves like a function call while operator[] does not.

So I have two questions :

  1. Why is the comma operator called inside an operator[] call and not inside an operator() call ?
  2. Is there a specific reason that prevents the compiler, first checking that f(a,b) does not match both the arity or the types of any f declaration, would not try to change the comma status and see if f(a.operator,(b)) leads to an acceptable synthax ? From my point of view, it would be the same kind of process that comes with types conversion.

Code example :

struct A{ };

struct B {
  A operator,(const B & other) const { return A(); }
};

struct C {
  C(){}
  C(const A & a){}
  void operator[](const A & a) const {}
  void operator()(const A & a) const {}
};

void f(const A & a){}

int main()
{
    B x,y;
    C z;

    //these do no compile because ',' in a function call is an argument separator
    //C(x,y);
    //f(x,y);

    //but this one compiles as z[x.operator,(y)]
    z[x,y];

    //and this one does not
    //z(x,y);

    //finally all of these do compile
    z((x,y));
    C((x,y));
    f((x,y));

   return 0;
}
VLAZ
  • 26,331
  • 9
  • 49
  • 67
yultan
  • 239
  • 1
  • 9
  • 4
    Look where "expressions" can be used in the syntax. A parameter pack in not an expression. A array index is an expression. The comma operator can only be used in expressions. Putting round brackets around stuff can in some cases turn them into expressions. For a full answer add the language-lawyer tag and/or read the standard. – Richard Critten Dec 19 '17 at 13:44
  • Well the `operator()` is called the ["*function call* operator"](http://en.cppreference.com/w/cpp/language/operators#Function_call_operator) as it is used (and *parsed*) as that. – Some programmer dude Dec 19 '17 at 13:44
  • That would not make the code more readable, don't you think? – Oliv Dec 19 '17 at 14:00
  • 2
    It would be incredibly confusing if the existence of an `operator,` could decide whether `z(x,y)` had one or two arguments. – molbdnilo Dec 19 '17 at 14:06
  • 1
    @molbdnilo: that would be an understatement. You wouldn't even know _which_ `f` to consider in the presence of overloads. And what to think about `f(x,y,z)` when `f` takes two parameters - do you try both `f(x,(y,z))` and `f((x,y),z)` ? – MSalters Dec 19 '17 at 14:24
  • @RichardCritten : Thank you for the tag suggestion. I did not expect the overloaded operator[] to keep the same array index semantic and so to require an expression. – yultan Dec 19 '17 at 14:30
  • @molbdnilo I agree it would be confusing, I am just asking out of curiosity. But for example my ide is highlighting custom operators so z(a,b) with one or two arguments would be visually different. – yultan Dec 19 '17 at 14:31
  • @MSalters : So the answer is that it would lead to combinatorial explosion in the general case, and such not feasible ? – yultan Dec 19 '17 at 14:32
  • @yultan concider the function call: `f(a, b, c, d, e ,f);` which is the comma-operator and which is the parameter-list-separator? Too hard for the compiler and to be backwards compatible they are all parameter-list-separator(s). Knowing that now concider: `g((a, b), (c), (d, e, f));` You should see that `f` takes 6 parameters and `g` takes only 3. With the extra commas (inside the extra brackets) in `g` being operators (the brackets around `c` are optional). – Richard Critten Dec 19 '17 at 14:42
  • @yultan A language must be understandable without concern for your preferred IDEs text-rendering habits. – molbdnilo Dec 19 '17 at 14:44
  • @yultan. Correct. Even the simplest overload pair `f(int)` and `f(int,int)` would need special rules to prevent `f(1,2)` form being ambiguous. – MSalters Dec 19 '17 at 15:20
  • Note that since C++23 `operator[]` will have same properties as function since multidimensional subscript has been introduced. – Marek R Jun 09 '22 at 14:10

2 Answers2

9

Why is the comma operator called inside an operator[] call and not inside an operator() call?

If you look grammatically, function calls are of the form postfix-expression ( expression-listopt ). An expression-list (which is an initializer-list, not to be confused with std::initializer_list) is a comma separated list of initializer-clauses (assuming there are at least two clauses). The commas are consumed by the parsing of the expression-list, where it has special meaning, rather than an part of an expression.

Indexing is of the form postfix-expression [ expr-or-braced-init-list ], there is no comma to be consumed at this point, so any comma that appears is necessarily part of the expression.

Is there a specific reason that prevents the compiler, first checking that f(a,b) does not match both the arity or the types of any f declaration, would not try to change the comma status and see if f(a.operator,(b)) leads to an acceptable syntax ?

I'm going to go with "sanity." Function calls are a really fundamental aspect of programs and they need to be straightforward. It would be insanely error-prone if you couldn't even know how many arguments you were passing. Especially if the builtin comma operator is used, which simply ignores arguments.

Moreover, it's very simple to force the use of a comma: add parentheses:

f(a, (t=3, t+2), c);

has three arguments, the second of which has value 5.

This works grammatically because the interior comma can't be a comma separating initializer-clauses, since (t=3 is not an initializer-clause.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Besides the general insanity of the idea, you don't know which `f`s you are looking at until you know the arguments (because of ADL), and if you don't know what the arguments are until you know which `f`s exist... – T.C. Dec 19 '17 at 19:45
0

The question and answer explain the behavior of commas in [] subscripts correctly for the time they were written.

However, for readers in the present and future:

With C++20 the use of , directly in [] was deprecated. It should now be parenthesized, meaning that instead of z[x,y] one should use z[(x,y)]. The comma is still interpreted as operator, (or the built-in comma operator) in either case though.

With C++23 the meaning of the comma in z[x,y] (without parentheses) will change. It will now be interpreted as operator[](x,y), not operator[](operator,(x,y)), just as z(x,y) has always been interpreted as operator()(x,y). Previously operator[] could only have one argument. With C++23 it will be allowed to have multiple arguments behaving the same way as operator().

This is a breaking change in the language, although use of the comma operator in a [] subscript had almost no practical uses and probably surprising/unintended behavior given the difference to () and the seemingly multi-dimensional indexing nature of the syntax.

user17732522
  • 53,019
  • 2
  • 56
  • 105