4

In this case

void f(int *);
void f(const int *);
...
int i;
f(&i);

the situation is pretty clear - f(int *) gets called which seems right.

However, if I have this (it was done like that by mistake(*) ):

class aa
{
public:
    operator bool() const;
    operator char *();
};

void func(bool);

aa a;
func(a);

operator char *() gets called. I cannot figure out why would such decision path be better than going to operator bool(). Any ideas?

(*) If const is added to the second operator, the code works as expected, of course.

Columbo
  • 60,038
  • 8
  • 155
  • 203

3 Answers3

5

Because for user-defined conversions with a conversion operator the conversion of the returned type to the destination type (i.e. char* to bool) is considered after the object argument conversion, i.e. the conversion of the object argument a to the implicit object parameter. [over.match.best]/1:

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi (F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

  • the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type.

So because the implicit object parameter, which is a reference, is not a const-reference for operator char*, it is a better match according to the first bullet point.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 1
    @LightnessRacesinOrbit [over.match.funcs]/4. – Columbo Jan 29 '15 at 21:50
  • I don't know why it says that but try it with code and you'll see. – Lightness Races in Orbit Jan 29 '15 at 22:35
  • `auto f = std::bind(&A::f, std::ref(a));` Huh, TIL. I've always passed a pointer. – Lightness Races in Orbit Jan 29 '15 at 22:47
  • @LightnessRacesinOrbit I think you missed the difference between `this` and the implicit object parameter/argument. The latter solely exists for overload resolution purposes. – Columbo Jan 29 '15 at 22:47
  • I must have been relying on a `bind` convenience overload. – Lightness Races in Orbit Jan 29 '15 at 22:47
  • @LightnessRacesinOrbit Whoops, that late edit was a fail. Anyway, this is not what I meant. Implicit object parameters are not existent in code itself (at least not the implicit object parameters in overload resolution). – Columbo Jan 29 '15 at 22:47
  • My point is that an implicit object parameter is technically part of the "raw" signature of the function. I was hoping to show that you couldn't bind to `A::f` with a reference rather than pointer as the implicit object parameter. The demonstration had nothing to do with the functionality of the `this` pointer. – Lightness Races in Orbit Jan 29 '15 at 22:48
  • @LightnessRacesinOrbit I suspect you mean another "implicit object parameter" than the one I do, one from another context? – Columbo Jan 29 '15 at 22:49
  • No.​​​​​​​​​​​​ There is only one implicit object parameter for non-static function members. – Lightness Races in Orbit Jan 29 '15 at 23:09
  • @LightnessRacesinOrbit "From another context". In some contexts the implicit object parameter is the `this` pointer. In the context of overload resolution the implicit object parameter is an lvalue reference (or rvalue reference, depending on the ref-qualifier) used as an additional parameter whose arguments conversion sequence is considered just as one from a "normal" parameter. – Columbo Jan 29 '15 at 23:27
  • _"In some contexts the implicit object parameter is the this pointer."_ For example? – Lightness Races in Orbit Jan 29 '15 at 23:27
  • @LightnessRacesinOrbit Debugging? It was just an example, anyway. For the standardese, take the definition given in the citation above. – Columbo Jan 29 '15 at 23:28
  • The standard does not discuss debugging. – Lightness Races in Orbit Jan 29 '15 at 23:30
  • @LightnessRacesinOrbit Ahh, ok. Then you were just simply wrong as the implicit object parameter defined in there is a reference used for purposes of overload resolution. – Columbo Jan 29 '15 at 23:33
  • Yes see my third comment above ^^^ :) – Lightness Races in Orbit Jan 29 '15 at 23:34
  • @LightnessRacesinOrbit Ahh, I must have misunderstood you then, my fault. :) Time to get some sleep. – Columbo Jan 29 '15 at 23:35
1

a is a non-const instance of aa so the non-const conversion operator is a better (exact) match than the const one (requires adding constness), even though the return types don't match as well.

Mark B
  • 95,107
  • 10
  • 109
  • 188
0

Your "aa" object is not const, so C++ prefers the non-const conversion.

If you make "aa" const, then the const bool() conversion operator will be used.

Ixrec
  • 966
  • 1
  • 7
  • 20
  • 1
    I kind of got there myself :) , but still don't understand why would that be a good design choice. Implicitly converting 'this' to const (to enlarge pool of suitable functions) seems like a much smaller thing than converting result. – Goran Mitrovic Jan 29 '15 at 20:57
  • That's the point: A function declared const is probably *not* suitable for use on a non-const object. If it seems like it is, you have either too many or too few "const"s in your code. – Ixrec Jan 29 '15 at 20:58
  • From a more cynical point of view, constness was invented long after the implicit bool/char/int/float/etc conversions were, and by then language designers knew it was important to make their features idiot-proof. – Ixrec Jan 29 '15 at 21:00
  • _"A function declared const is probably not suitable for use on a non-const object."_ I can call `size()` on a non-const `std::string` though. – Oktalist Jan 29 '15 at 21:12
  • Damn, I put the "non" in the wrong place and now it's too late to edit the comment. – Ixrec Jan 29 '15 at 21:14