16

次のコード例を検討してください。

#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 ビット幅でなければならないためです。では曖昧さはどこにあるのでしょうか?

「size_t x」の代わりに「unsigned long x」を使用して同じことを試みると、同様のあいまいなエラー メッセージが表示されます。

Linux/Solaris システムでは、さまざまな GCC バージョン、さまざまなアーキテクチャなどでテストを行っています。あいまいさは報告されていません (そして、各アーキテクチャで適切なオーバーロードが使用されています)。

これは Mac OS X のバグですか、それとも機能ですか?

4

2 に答える 2

11

Mac OS では、これらのタイプは次のように定義されます。

typedef unsigned int         uint32_t;
typedef unsigned long long   uint64_t;

assize_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
于 2012-07-22T20:53:53.653 に答える
6

本当にしたい場合は、次のように目的のセマンティクスを実装できます。

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

これが良い考えだとは言いません。できると言っているだけです。

コンパイラに「これらの 2 つの型は同じですか。私の言いたいことはわかります。馬鹿げた行動をしないでください」とコンパイラに尋ねる良い標準 C++ の方法があるかもしれませんが、もしあれば、私にはわかりません。


2020 更新: マクロを使用せずに、より慣用的に実行できたはずです。C++14 では省略形が提供enable_if_tされ、C++17 ではis_integral_v次のようになりました。

template<int Bits, class T>
constexpr bool is_uint_v = 
    sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;

template<class T> auto f(T) -> std::enable_if_t<is_uint_v<32, T>, int>
    { return 1; }

template<class T> auto f(T) -> std::enable_if_t<is_uint_v<64, T>, int>
    { return 2; }

そして C++20 では even-shorter-shorthand がありrequiresます:

template<int Bits, class T>
constexpr bool is_uint_v =
    sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;

template<class T> int f(T) requires is_uint_v<32, T> { return 1; }
template<class T> int f(T) requires is_uint_v<64, T> { return 2; }

さらに短い省略形の「省略された関数テンプレート」(ただし、これは率直に言って難読化されており、実際にはお勧めしません):

template<class T, int Bits>
concept uint =
    sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;

int f(uint<32> auto) { return 1; }  // still a template
int f(uint<64> auto) { return 2; }  // still a template
于 2014-03-25T20:55:49.380 に答える