Search code examples
c++operator-overloadinglanguage-lawyercomma-operator

Why is comma operator called inside operator [] but not inside operator ()?


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;
}

Solution

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