简体   繁体   中英

Ambiguous Call of “long unsigned int” for “uint32_t”

I have a bunch of functions overloaded for all of the [u]int{8|16|32|64}_t types:

std::string f( uint8_t) { /*something*/ }
std::string f(  int8_t) { /*something*/ }
std::string f(uint16_t) { /*something*/ }
std::string f( int16_t) { /*something*/ }
std::string f(uint32_t) { /*something*/ }
std::string f( int32_t) { /*something*/ }
std::string f(uint64_t) { /*something*/ }
std::string f( int64_t) { /*something*/ }
//A few more overloads with a few more types (bool, float, const char*, etc.)

I now call the function name with an argument of type long unsigned int :

template <typename type_blah> class Something { public:
        //...
        std::string call_f(void) const {
            return f(*((type_blah*)(internal_variable)));
        }
        //...
};

This produces an error:

error: call of overloaded 'f(long unsigned int&)' is ambiguous


This happens, I suppose, because unsigned int and uint32_t are different types. But, I can't overload the function more for long unsigned int because this is a redundant definition. Ie:

std::string f(long unsigned int) { /*something*/ }

. . . produces:

error: 'std::string f(uint32_t)' previously defined here


It seems the type mechanisms are working against each other: it can't figure out which conversion to use because each conversion is equally valid, but a no-conversion overload can't be defined because it already has been.

I can't cast the argument for various reasons. Is there a way out of this?

Platform is g++ MinGW x86 running on Windows 7 x86-64.

What platform are you using?

On Windows (Visual Studio 2010), unsigned long int is a distinct type from the others you mentioned.

Adding an overload specifically for that type resolved the error. This answer (and/or Google) may shed more light on the issue: Type of unsigned long is different from uint32_t and uint64_t on Windows (VS2010) .

I defined an overload for unsigned long int like so:

std::string f( unsigned long int val )
{
  // Check that we chose the correct corresponding type
  // (This may vary by platform!)
  assert( sizeof( unsigned long int ) == sizeof( uint32_t ) );

  return f( static_cast<uint32_t>( val ) );
}

...tested in Visual Studio 2010 like so:

void main()
{
  std::cout << "sizeof( unsigned long int ): " << sizeof( unsigned long int ) << std::endl;
  std::cout << "sizeof( uint32_t ): "          << sizeof( uint32_t )          << std::endl;

  unsigned long int x = 1u;
  std::cout << f( x ) << std::endl;
}

...and got the expected result:

sizeof( unsigned long int ): 4
sizeof( uint32_t ): 4
uint32_t

The following is my "test" based on the code provided above:

#include <string>
#include <cstdint>
#include <iostream>

std::string f( uint8_t) { return "ui8";}
std::string f(  int8_t) { return "i8";}
std::string f(uint16_t) { return "ui16";}
std::string f( int16_t) { return "i16";}
std::string f(uint32_t) { return "ui32";}
std::string f(unsigned long long int) { return "unsigned long long";}
std::string f(unsigned long int) { return "unsigned long";}
std::string f( int32_t) { return "i32";}
//std::string f(uint64_t) { return "ui64";}
std::string f( int64_t) { return "i64";}

int main()
{
    unsigned long x = 42;
    unsigned y = 17;
    uint32_t z = 9;
    uint64_t w = 135;
    std::cout << "x: "<< f(x) << " y: " << f(y) << " z: " << f(z) << " w: " << f(w) << std::endl;
}

Sample output:

$ clang++ ambig.cpp -std=c++0x -Wall -m64
$ ./a.out 
x: unsigned long y: ui32 z: ui32 w: unsigned long
$ clang++ ambig.cpp -std=c++0x -Wall -m32
$ ./a.out 
x: unsigned long y: ui32 z: ui32 w: unsigned long long

(I copied my run with clang++, but the results are the same for g++)

This makes sure that both the unsigned long and unsigned long long types are covered. Unfortunately, one of those is uint64_t , so it has to be removed.

If you do declare variables as unsigned long , you must supply a function that takes exactly that - and relying on it being equivalent to uint32_t is probably incorrect - even if they are the same bitsize.

Since you're defining overloads for (nearly?) every type, perhaps your function should be a template instead?

template < typename T >
std::string f( T )
{
  /*something*/
}

If there is one set of code that works for all - or even most - types, that would save you a lot of work as well.

If for some reason you still need to force the call with the unsigned long int value to treat it as some other type (like uint32_t ), you could specify the template instantiation at the call-site:

return f<uint32_t>( *internal_variable );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM