7

Consider the following code:

struct A {
    void operator++() const {}
};

void operator++(const A&) {}


int main () {
    const A ca;
    ++ca; // g++ Error (as expected): ambiguous overload for ‘operator++’

    A a;
    ++a; // g++ Warning: "ISO C++ says that these are ambiguous,
         // even though the worst conversion for the first is better
         // than the worst conversion for the second"
         // candidate 1: void operator++(const A&)
         // candidate 2: void A::operator++() const
}

Why is g++ only issuing a warning and not an error on ++a? In other words, how is the non-member function a better fit than the member function?

Thank you!

bap
  • 118
  • 6
  • 2
    g++ basically thinks that this ambiguity can safely be resolved, even though the standard says otherwise; if you add `-pedantic-errors`, it should error. – Collin Dauphinee Mar 21 '14 at 00:00
  • I don't think the comment you mentioned has something to do with what happens here, since there's no conversion in your example. Rather, I think the gcc message has something to do with the *implicit object parameter* or *implied object argument*, but I'm not quite sure where the difference is to the real parameter of the non-member function (in the Standard). – dyp Mar 21 '14 at 00:13
  • Looking through questions where a similar error message occurs, it seems it (typically) refers to a situation where there are at least *two* parameters, e.g. http://stackoverflow.com/a/3525172/420683 – dyp Mar 21 '14 at 00:18
  • *"It looks like g++ is creating a temporary object that can not be bound to a non-const reference"* Where is a non-const reference here? And where is g++ creating a temporary? (or rather: why do you conclude that?) – dyp Mar 21 '14 at 00:24
  • @dyp I was referring to the comment I linked which states "the compiler has to create a temporary DepthDescriptor object. It's a rule of C++ that you cannot bind a temporary object to a non-const reference." But I couldn't find more info about that online. Thank you for your link I will check it out. – bap Mar 21 '14 at 00:32
  • 1
    In *that* questions's context, yes, it has to create a temporary, for the *conversion* that's required there (from the enumeration type to the class type). – dyp Mar 21 '14 at 00:36
  • @dyp gotcha thank you. I will remove the link from my original post to not confuse people. – bap Mar 21 '14 at 00:47

2 Answers2

2

If I were to guess, the member function causes a pointer qualification conversion from A * to A const * when initializing this, whereas the non-member binds an A const & reference to a non-const object, which isn't really a conversion at all but is merely a non-preferred case during overload resolution.

That message comes about when the paths leading from the argument type to the respective parameter types involve different kinds of conversions. The standard refuses to compare apples to oranges, e.g. pointer qualification vs. reference binding or integral promotion vs. conversion operator, but GCC is willing.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 1
    The implicit object parameter for the member function is (should be) of a reference type [over.match.funcs]/4, so in both cases we should have an `A const&` parameter. – dyp Mar 21 '14 at 01:09
  • @dyp What happens in GCC is GCC's business. This is just a retroactive explanation. – Potatoswatter Mar 21 '14 at 01:13
  • Ah, ok. That's why I inserted a "should be" ;) So you're guessing that gcc uses a pointer type as an implicit object parameter? (That would be nonconforming..) – dyp Mar 21 '14 at 01:15
  • 2
    @dyp Overloading implementation involves lots of special cases, so a pointer might pop up somewhere. The standard wouldn't care as long as the well-formed cases are all interpreted correctly and the ill-formed cases all produce at least a warning message. But, this is only my hypothesis and I don't plan to dig in further. – Potatoswatter Mar 21 '14 at 01:17
1

In c++ you can separate methods (and operators) for const and not const, When you don't do it, the compiler can search for the "best fit". The best fit for the not const is the const.

take a look at this example:

struct S{
  void f(){cout<<"f";}
  void f()const{cout<<"f-const";}
};

int main(){
   const S sc;
   S s;
   s.f();
   sc.f();
   return 0;
}

the output for the 1st print will be "f" but for the 2nd it will be "f-const"

if I remove the not const method from the struct, I'll get the same output for the both objects. and this is because the function is what called "best fit", since it can add the "constiness" to a not const object. (can't remove the const method since it won't fit at all...)

In your code there is no explicit operator for not const so when it look for the "best fit" it can choose from the options, and take what look like the best. you got a warning since there was 2 fits, but still it choose one of them, I don't know why one look better then the other... but for the const it have two explicit functions, the compiler can't choose when have explicit method! that is why you've got an error.

If you want the same behavior add also explicit not const operator, like this:

struct A {
    void operator++() const {}
    void operator++(){};
};

void operator++(const A&) {}
void operator++(A&) {}

Now you'll get the same error for both.

SHR
  • 7,940
  • 9
  • 38
  • 57
  • 2
    Thank you for taking the time to write this. But my question really is: how is the non-member function a better fit than the member function? – bap Mar 21 '14 at 00:44
  • Why not? They look the same fit to me. maybe because it prefix operator it harder to assign the operator to the struct? The point is not why take one and not the other. if you let it to choose it take the freedom to decide what to choose, but maybe some other compiler would choose the member operator, since the c++ standard is not so clear in this case, therefor you've got the warning. – SHR Mar 21 '14 at 00:56
  • @SHR According to the warning, the standard is clear. But OP is wondering how the implementer arrived at this conclusion. You are not answering that question. – pmr Mar 21 '14 at 01:12
  • Both conversions looks the same to the compiler, When they are implicit and two conversions are the same it take one, no matter which (the global one maybe?, I'm quite sure that if you call it from the struct context it would choose the member one), and give a warning. When it is explicit, it have to rise an error, as happened in the const case. – SHR Mar 21 '14 at 01:37