Why is there ambiguity between uint32_t and uint64_t when using size_t on Mac OS X?

maxschlepzig picture maxschlepzig · Jul 22, 2012 · Viewed 12.9k times · Source

Consider following example code:

#include <iostream>
#include <inttypes.h>

using namespace std;

int f(uint32_t i)
{
  return 1;
}
int f(uint64_t i)
{
  return 2;
}

int main ()
{
  cout << sizeof(long unsigned) << '\n';
  cout << sizeof(size_t) << '\n';
  cout << sizeof(uint32_t) << '\n';
  cout << sizeof(uint64_t) << '\n';
  //long unsigned x = 3;
  size_t x = 3;
  cout << f(x) << '\n';
  return 0;
}

This fails on Mac OSX with:

$ g++ --version
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
$ make test
g++     test.cc   -o test
test.cc: In function 'int main()':
test.cc:23: error: call of overloaded 'f(size_t&)' is ambiguous
test.cc:6: note: candidates are: int f(uint32_t)
test.cc:10: note:                 int f(uint64_t)
make: *** [test] Error 1

Why? Because 'size_t' should be unsigned and either 32 bit or 64 bit wide. Where is the ambiguity then?

Trying the same with 'unsigned long x' instead of 'size_t x' results in an analogous ambiguity error message.

On Linux/Solaris systems, testing with different GCC versions, different architectures etc. there is no ambiguity reported (and the right overload is used on each architecture).

Is this a Mac OS X bug or a feature?

Answer

trojanfoe picture trojanfoe · Jul 22, 2012

Under Mac OS, those types are defined as:

typedef unsigned int         uint32_t;
typedef unsigned long long   uint64_t;

Where as size_t is defined as __SIZE_TYPE__:

#if defined(__GNUC__) && defined(__SIZE_TYPE__)
typedef __SIZE_TYPE__       __darwin_size_t;    /* sizeof() */
#else
typedef unsigned long       __darwin_size_t;    /* sizeof() */
#endif

So if you change your code to:

#include <iostream>
#include <inttypes.h>

using namespace std;

int f(uint32_t i)
{
  return 1;
}
int f(uint64_t i)
{
  return 2;
}

int f (unsigned long i)
{
  return 3;
}

int main ()
{
  cout << sizeof(unsigned long) << '\n';
  cout << sizeof(size_t) << '\n';
  cout << sizeof(uint32_t) << '\n';
  cout << sizeof(uint64_t) << '\n';
  //long unsigned x = 3;
  size_t x = 3;
  cout << f(x) << '\n';
  return 0;
}

And run it, you will get:

$ g++ -o test test.cpp
$ ./test
8
8
4
8
3