99

C++ の型の特性を使用しているときに奇妙な動作を経験しましたが、問題をこの風変わりな小さな問題に絞り込みました。誤解を招く可能性があるため、これについて多くの説明を行います。

次のようなプログラムがあるとします。

#include <iostream>
#include <cstdint>

template <typename T>
bool is_int64() { return false; }

template <>
bool is_int64<int64_t>() { return true; }

int main()
{
 std::cout << "int:\t" << is_int64<int>() << std::endl;
 std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;
 std::cout << "long int:\t" << is_int64<long int>() << std::endl;
 std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;

 return 0;
}

GCC (および 32 ビットと 64 ビットの MSVC) を使用した 32 ビット コンパイルの両方で、プログラムの出力は次のようになります。

int:           0
int64_t:       1
long int:      0
long long int: 1

ただし、64 ビット GCC コンパイルの結果のプログラムは次のように出力されます。

int:           0
int64_t:       1
long int:      1
long long int: 0

long long intは符号付き 64 ビット整数であり、すべての意図と目的でlong intint64_t型と同一であるため、論理的にint64_tlong intとは同等の型になるため、これは興味深いlong long intことです。これらの型を使用するときに生成されるアセンブリは同一です。一見すると、そのstdint.h理由がわかります:

# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

64 ビット コンパイルでint64_tは、 islong intであり、long long int(明らかに) ではありません。

この状況の修正は非常に簡単です。

#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif

しかし、これはひどくハックであり、うまくスケーリングできません (実体の実際の機能uint64_tなど)。 だから私の質問は次のとおりlong long intです。コンパイラに aも aint64_tであることを伝える方法はありlong intますか?


私の最初の考えでは、C/C++ の型定義が機能する方法のために、これは不可能であるということです。基本的なデータ型の型等価性をコンパイラに指定する方法はありません。これはコンパイラの仕事であり (そして、それを許可すると多くのことが壊れる可能性があります)、一方向にしか進まないためtypedefです。

また、ここで回答を得ることにもあまり関心がありません。これは非常に極端なエッジ ケースであり、例がひどく不自然である場合に誰も気にしないと思われるからです (つまり、これはコミュニティ wiki であるべきということですか?)。 .


追加: 次のような簡単な例ではなく、部分的なテンプレートの特殊化を使用している理由:

void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}

long long intは暗黙的に に変換可能であるため、上記の例は引き続きコンパイルされint64_tます。


追加: これまでの唯一の答えは、型が 64 ビットかどうかを知りたいという前提です。私はそれを気にかけていると人々に誤解させたくなかったので、おそらくこの問題がどこで現れるかについてもっと多くの例を提供するべきでした.

template <typename T>
struct some_type_trait : boost::false_type { };

template <>
struct some_type_trait<int64_t> : boost::true_type { };

この例でsome_type_trait<long int>は、 になりますが、そうboost::true_typesome_type_trait<long long int>はありません。これは C++ の型の考え方では理にかなっていますが、望ましくありません。

別の例では、次のような修飾子same_typeを使用しています (C++0x の概念で使用するのはかなり一般的です)。

template <typename T>
void same_type(T, T) { }

void foo()
{
    long int x;
    long long int y;
    same_type(x, y);
}

C++ は型が異なることを (正しく) 認識するため、この例はコンパイルに失敗します。g++ は次のようなエラーでコンパイルに失敗します: no matching function call same_type(long int&, long long int&)

なぜこれが起こっているのかを理解していることを強調したいと思いますが、コードをあちこちに繰り返さなくても済む回避策を探しています。

4

3 に答える 3

51

このようなものを見るために 64 ビットに移行する必要はありません。int32_t一般的な 32 ビット プラットフォームで検討してください。それは asまたは atypedefとして 'edかもしれませんが、明らかに一度に 2 つのうちの 1 つだけです。もちろん、異なるタイプです。intlongintlong

int == int32_t == long32 ビット システムでの回避策がないことを理解するのは難しくありません。同じ理由で、long == int64_t == long long64 ビット システムで作成する方法はありません。

可能であれば、考えられる結果は、オーバーロードされたコードにとってはかなり苦痛であり、foo(int)突然、同じオーバーロードに対して2つの定義がありましたか?!foo(long)foo(long long)

正しい解決策は、通常、テンプレート コードが正確な型に依存するのではなく、その型のプロパティに依存することです。same_type特定のケースでは、ロジック全体が問題ない可能性があります。

long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);

つまり、 とまったく同じ場合、オーバーロードfoo(int64_t)は定義されません。foo(long)

[編集] C++11 では、これを書くための標準的な方法があります。

long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);

[編集] または C++20

long foo(long x);
int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
于 2010-11-12T16:10:39.740 に答える
6

型が int64_t と同じ型かどうかを知りたいですか、それとも何かが 64 ビットかどうかを知りたいですか? 提案された解決策に基づいて、後者について質問していると思います。その場合、私は次のようなことをします

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
于 2010-11-12T03:01:26.143 に答える