7

I have two functions:

void DoSomething( const tchar* apsValue )
void DoSomething( size_t aiValue )

Now I want to pass '0' as a size_t:

DoSomething(0);

The compiler throws an error: "ambiguous call to overloaded function"

To solve this, I can use static_cast, for instance:

DoSomething(static_cast<size_t>(0));

Or simple:

DoSomething(size_t(0));

Is one of them better than the other? Are there any other approaches to solve this?

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • 1
    static_cast is anytime better than c-style cast, where it is applicable. – iammilind Feb 21 '12 at 12:32
  • 4
    @iammilind size_t(0) isn't a c-style cast. It constructs a new size_t with '0' as value – Martin Brandl Feb 21 '12 at 12:38
  • @iammilind Why? `static_cast` is preferable to a C style cast where there may be an issue of what type the C style cast resolves to: for pointers or references. Otherwise... would you write `static_cast< MyClass >( 42 )` or `MyClass( 42 )`, when you want a temporary instance of `MyClass`? – James Kanze Feb 21 '12 at 12:41
  • In this specific case, you might be able to write `DoSomething(0u)` for the size_t, but that is not a general solution for similar cases. – Mr Lister Feb 21 '12 at 12:52
  • @JamesKanze, `...where it is applicable` – iammilind Feb 21 '12 at 13:58
  • @MrLister, `DoSomething(0ul)` or `DoSomething(0ull)`, depending on the build target. Messy, isn't it? – Janusz Lenar Jan 23 '13 at 09:59
  • Possible duplicate of [Call of overloaded function is ambiguous](https://stackoverflow.com/questions/4672152/call-of-overloaded-function-is-ambiguous) – rdb Jun 18 '18 at 18:05

3 Answers3

7

It's ambiguous because 0 has type int, not size_t. It can convert to either size_t or a pointer, so if you have an overload of both, it's ambiguous. In general, I would recommend that if you have overloaded functions, and one of them can take an integral type, you add an overload for int, maybe along the lines of:

inline void DoSomething( int aiValue )
{
    DoSomething( static_cast<size_t>( aiValue ) );
}

Integral literals have type int by default (unless they're too big to fit into an int), and by providing an exact match, you avoid any ambiguity.

jweyrich
  • 31,198
  • 5
  • 66
  • 97
James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

Reason for ambiguity: NULL has the numeric value 0.

If in case you want void DoSomething( const tchar* apsValue ) when passing 0 as the parameter, nullptr will be helpful. Check this What exactly is nullptr?

Community
  • 1
  • 1
omggs
  • 1,163
  • 1
  • 11
  • 23
  • But `NULL` isn't the cause of the ambiguity in this question; `0` is. The new `nullptr` won't help here since the goal was never to call the pointer version of the function anyway. – Rob Kennedy Feb 21 '12 at 13:02
  • Agreed! But the compiler is finding it ambiguous because it could be NULL's value or just `int` whose value is `0`. I will try rephrasing it. – omggs Feb 21 '12 at 13:24
1
#include <iostream>
#include <stddef.h>
using namespace std;

void DoSomething( char const* apsValue ) { cout << "ptr" << endl; }
void DoSomething( size_t aiValue ) { cout << "int" << endl;}

template< class Type > Type runtime_value( Type v ) { return v; }
int null() { return 0; }
template< class Type > Type* nullPointerValue() { return 0; }

int main()
{
    // Calling the integer argument overload:
    int dummy = 0;
    DoSomething( size_t() );
    DoSomething( runtime_value( 0 ) );
    DoSomething( null( ) );
    DoSomething( dummy );
    static_cast< void(*)( size_t ) >( DoSomething )( 0 );

    // Calling the pointer argument overload:
    DoSomething( nullptr );
    DoSomething( nullPointerValue<char>() );
    static_cast< void(*)( char const* ) >( DoSomething )( 0 );
}

It might seem surprising that this works, but it's not just implicit type conversion at work. It's also that a compile time constant 0 of integral type converts implicitly to nullpointer. E.g., the null() function avoids that because the result is not a compile time constant.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331