長いint vs. long int vs. int64_t in c ++
質問
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;
}
32ビットコンパイル(および32ビットおよび64ビットMSVC)の両方で、プログラムの出力は次のとおりです。
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 int
と int64_t
論理的には、 int64_t
, long 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
は long 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です int64_t
, 、 と同じように long int
は?
私の最初の考えは、C/C ++タイプの定義が機能する方法により、これは不可能だということです。コンパイラのジョブである(そしてそれが多くのことを破ることができる)ので、コンパイラへの基本データ型のタイプの等価性を指定する方法はありません。 typedef
片道しか行きません。
私はここで答えを得ることにあまり関心がありません。なぜなら、これは例が恐ろしく考案されていないときに誰も気にすることはないと思う超大型エッジケースだからです(それはこれがコミュニティのウィキであるべきだということですか?) 。
追加: :次のような簡単な例ではなく、部分的なテンプレートの専門化を使用している理由
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>
aになります boost::true_type
, 、 しかし some_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 ++は次のようなエラーでコンパイルできません:一致する関数呼び出しなし same_type(long int&, long long int&)
.
私が理解していることを強調したいと思います どうして これは起こっていますが、私はあちこちでコードを繰り返すことを強いられない回避策を探しています。
解決
このようなものを見るために64ビットに行く必要はありません。検討 int32_t
一般的な32ビットプラットフォームで。それは可能性があります typedef
'as int
またはaとして long
, 、しかし、明らかに2つのうちの1つだけが一度に1つだけです。 int
と long
もちろん、異なるタイプです。
回避策がないことを見るのは難しくありません int == int32_t == long
32ビットシステム。同じ理由で、作る方法はありません long == int64_t == long long
64ビットシステム。
可能であれば、考えられる結果は、過負荷になったコードにとってかなり苦痛です foo(int)
, foo(long)
と foo(long long)
- 突然、同じ過負荷の2つの定義がありますか?!
正しい解決策は、テンプレートコードが通常、正確なタイプではなく、そのタイプのプロパティに依存するべきであることです。全体 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);
他のヒント
タイプがint64_tと同じタイプであるかどうか、または何かが64ビットかどうか知りたいですか?提案されたソリューションに基づいて、後者について尋ねていると思います。その場合、私はようなことをします
template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
だから私の質問は、長いintもint64_tであることをコンパイラに伝える方法はありますか?
これは良い質問や問題ですが、答えはノーだと思います。
また、a long int
そうではないかもしれません long long int
.
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
これはlibcだと思います。私はあなたがより深く行きたいと思うと思います。
32ビットコンパイル(および32ビットおよび64ビットMSVC)の両方で、プログラムの出力は次のとおりです。
int: 0 int64_t: 1 long int: 0 long long int: 1
32ビットLinuxは、ILP32データモデルを使用します。整数、ロング、ポインターは32ビットです。 64ビットタイプはaです long long
.
Microsoftは、範囲を文書化しています データ型の範囲. 。言う long long
に相当します __int64
.
ただし、64ビットGCCコンパイルから生じるプログラムは出力されます。
int: 0 int64_t: 1 long int: 1 long long int: 0
64ビットLinuxが使用します LP64
データ・モデル。ロングは64ビットです long long
64ビットです。 32ビットと同様に、Microsoftは範囲を文書化します データ型の範囲 そして長い長いです __int64
.
あります ILP64
すべてが64ビットであるデータモデル。あなたはあなたの定義を得るためにいくつかの追加の仕事をしなければなりません word32
タイプ。似たような論文も参照してください 64ビットプログラミングモデル:なぜLP64?
しかし、これは恐ろしくハッキッシュであり、十分にスケーリングしません(物質の実際の機能、UINT64_Tなど)...
ええ、それはさらに良くなります。 GCCは、64ビットタイプを取ることになっている宣言を混合および一致させるため、特定のデータモデルに従っている場合でも、トラブルに巻き込まれやすくなります。たとえば、以下はコンパイルエラーを引き起こし、使用するように指示します -fpermissive
:
#if __LP64__
typedef unsigned long word64;
#else
typedef unsigned long long word64;
#endif
// intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864)
// extern int _rdrand64_step(unsigned __int64 *random_val);
// Try it:
word64 val;
int res = rdrand64_step(&val);
結果:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
だから、無視してください LP64
に変更してください:
typedef unsigned long long word64;
次に、定義する64ビットアームIoTガジェットにさまよう LP64
ネオンを使用してください:
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'