Подтвердить что ты не робот

Почему существует двусмысленность между uint32_t и uint64_t при использовании size_t в Mac OS X?

Рассмотрим следующий пример кода:

#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;
}

В Mac OSX это не работает:

$ 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

Почему? Потому что 'size_t' должен быть неподписанным и 32-битным или 64-битным. Где тогда двусмысленность?

Попытка того же с "unsigned long x" вместо "size_t x" приводит к аналогичное сообщение об ошибке двусмысленности.

В системах Linux/Solaris, тестировании с разными версиями GCC, разных архитектурах и т.д. нет двусмысленности (и правильная перегрузка используется для каждой архитектуры).

Является ли это ошибкой Mac OS X или функцией?

4b9b3361

Ответ 1

В Mac OS эти типы определяются как:

typedef unsigned int         uint32_t;
typedef unsigned long long   uint64_t;

Где как size_t определяется как __SIZE_TYPE__:

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

Итак, если вы измените свой код на:

#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;
}

И запустите его, вы получите:

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

Ответ 2

Если вы действительно этого захотите, вы можете реализовать свою желательную семантику следующим образом:

#define IS_UINT(bits, t) (sizeof(t)==(bits/8) && \
                          std::is_integral<t>::value && \
                          !std::is_signed<t>::value)

template<class T> auto f(T) -> typename std::enable_if<IS_UINT(32,T), int>::type
{
  return 1;
}

template<class T> auto f(T) -> typename std::enable_if<IS_UINT(64,T), int>::type
{
  return 2;
}

Не сказать, что это хорошая идея; просто сказал, что вы можете это сделать.

Может быть хороший стандарт-С++ способ спросить компилятора: "Эти два типа одинаковы, вы знаете, что я имею в виду, не действуйте немой со мной", но если есть, я не знаю,.