2

Check it out this code:

struct A
{
    A operator+(A const& a) { cout << 1 << endl; return A(); }
    A& operator++() { cout << 2 << endl; return *this; }
    A operator++(int) { cout << 3 << endl; return *this; }
    bool operator!() { cout << 4 << endl; return true; }

};

A operator+(A const& a, A const& b)
{ cout << 5 << endl; return A(); }

A& operator++(A& a) { cout << 6 << endl; return a; }
A operator++(A const& a, int) { cout << 7 << endl; return A(); }
bool operator!(A const& a) { cout << 8 << endl; return false; }

int main()
{
    A a, b;

    a + b; // Prints 1 instead 5
    ++a;   // Ambiguity
    a++;   // Prints 3 instead 7
    !a;    // Prints 4 instead 8

    return 0;
}

In every case, the in-class overload of an operator is choosen against any other out-class overload of the same operator, but the preincrement operator is different: it causes an ambiguity between the in-class and out-class overload.

Why?

ABu
  • 10,423
  • 6
  • 52
  • 103

1 Answers1

4

Your post-increment member operator is not a const member, whereas the non-member one const A& parameter. There is no ambiguity, and the non-const version chosen. The same does not apply for the pre-increment version. Both member and non-member bind to a non-const A, hence the ambiguity.

Look:

// non-const member. Does not require A operand to be const
A operator++(int)

// non-member. Requires A operand to be const
A operator++(A const& a, int)
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • This applies to the `+` and `!` operators as well. Declare the member functions `const`, and the corresponding calls become ambiguous. – Wintermute May 06 '15 at 21:13
  • Pretty obvious. I seem a rookie. By the way, why the non-const version is choosen against the non-member function? For being "less cv-qualified"? After all, a cv-comparision doesn't apply for member and non-member overloads. In my understanding, all forms (const/non-const member function) should provoke an ambiguity with non-class versions. – ABu May 06 '15 at 21:18
  • "After all, a cv-comparision doesn't apply for member and non-member overloads." I'm not sure what exactly that means, but it could be the source of misunderstanding. – juanchopanza May 06 '15 at 21:20
  • @Peregring-lk Non-const overloads are chosen in this case because `a` is not `const` and the rules of the language state those overloads are preferred over `const` ones, all other things equal. – juanchopanza May 06 '15 at 21:22
  • I meant: in order to choose an overload (for a regular function-member, not an operator), if an object is non-const, binding to a const version requeries a const-conversion, so, the non-const version is a best match if both exist. Now, concerning operators, we haven't two const/non-const member operator overloads, but two member/non-member operators. What I don't understand is why the member version is a better match than the non-member one (when parameters are the same). Are there rules stating it explicity (member operator best), or does it happen due to a different reason? – ABu May 06 '15 at 21:29
  • @Peregring-lk Because the members don't have a `const` operand that the non-members do. – juanchopanza May 06 '15 at 21:31
  • Non-member functions/operators can't be const qualified. It has no sense at all. So, a comparision between then, using cv qualifications as discriminant, has no sense again. – ABu May 06 '15 at 21:35
  • @Peregring-lk Non members can have const parameters. The corresponding parameters of the member versions are not const, because the members aren't const. – juanchopanza May 06 '15 at 21:37
  • Ok, I see. You meant all the time the implicit parameter: if the member overload is `const`, the implicit parameter -the first one, is also `const`, as it happens in the non-member version. – ABu May 06 '15 at 21:44