12

I'm pretty sure this must have been here already, but I didn't find much information on how to solve this kind of problem (without casting on the call):

Given two overloads, I want that a call with function with a literal 0 always calls the unsigned int version:

void func( unsigned int ) {
    cout << "unsigned int" << endl;
}

void func( void * ) {
    cout << "void *" << endl;
}

func( 0 ); // error: ambiguous call

I understand why this happens, but I don't want to write func( 0u ) or even func( static_cast(0) ) all the time. So my questions are:

1) Is there a recommended way to do this in general?

2) Is there any problem with doing it the following way and what is the reason that this to works?

void func( unsigned int ) {
    cout << "unsigned int" << endl;
}

template <typename T>
void func( T * ) {
    static_assert( std::is_same<T, void>::value, "only void pointers allowed" );
    cout << "void *" << endl;
}

func( 0 ); // calls func( unsigned int )!
mfya
  • 352
  • 4
  • 9

4 Answers4

2

What you're doing in 2) works and is probably the better way to do it.

In situations where you don't want to change the functions, you can do an explicit cast to give the compiler a hint:

func((void *) 0);
func((unsigned int) 0);
EboMike
  • 76,846
  • 14
  • 164
  • 167
2

I suggest you look at the null pointer from C++0x (see this). It defines a class representing null pointers of any type. The example you just gave was actually one motivating the inclusion of nullptr_t (the class) / nullptr (the value) to C++0x. It actually lets you disambiguate this call by putting 0 when wanting the unsigned int version, and nullptr when you want the other.

You may just implement this trick in a little utility class until your compiler supports it (or just use it if your compiler implements this part of the next standard).

Community
  • 1
  • 1
Alp Mestanogullari
  • 1,042
  • 7
  • 13
  • 1
    "putting 0 when wanting the unsigned int version" - But I think `func(0)` is still ambiguous in C++0x. Both `int` to `unsigned int` and null-pointer-constant to `void*` or to `std::nullptr_t` are Conversions and neither is better than the other for overload resolution. – aschepler Jan 06 '11 at 00:37
  • I thought so at the beginning, but I refer you to this : http://www.devx.com/cplus/10MinuteSolution/35167/1954 (read the "See how nullptr elegantly solves the overload resolution problem shown earlier:" section) – Alp Mestanogullari Jan 06 '11 at 00:44
  • 1
    `nullptr` is a pointer-like object but that doesn't mean that `int`s are no longer implicitly convertible to pointers. It means that when you write `nullptr` you aren't actually writing `0` in a roundabout way. – Max Lybbert Jan 06 '11 at 04:52
  • @Max, I agree, but the link Alp provides indicates otherwise. It says 0 will not longer convert to the pointer (or at least have lower overload priority), but I suspect that would actually break a lot of code. – edA-qa mort-ora-y Jan 06 '11 at 08:20
  • Thanks but this does not help me, because I want that a plain literal 0 always to be passed to the non-pointer overload. The actual purpose is an overloaded operator[] which allows indexing by index but also by char * (for string literals) where a nullptr is never a valid argument. – mfya Jan 06 '11 at 15:13
  • couldn't you index by std::string ? (thus forbidding 0) – Alp Mestanogullari Jan 06 '11 at 23:14
1

1) Is there a recommended way to do this in general?

Yes, I would do it the way you did in 2). I don't think there is any deeper meaning as to why 2) works. The type int simply doesn't match T*, so it has no way to find out T. It will therefor ignore the template.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
1

1) Is there a recommended way to do this in general?

The problem is simply that the literal 0 is an int, not an unsigned int, and there are valid conversions from int to unsigned int and from int to void*. I can't say that there's a recommended way to handle the problem. Aside from the ways you've already found, you could also add another overload:

void func(int i) 
{
    assert(i >= 0);
    return func(static_cast<unsigned int>(i));
}

2) Is there any problem with doing it the following way and what is the reason that this to works?

The template trick works because the rules for resolving calls to functions that are overloaded and have templated versions are designed to prefer non-templated versions of the overloaded functions.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69