質問

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 intint64_t 論理的には、 int64_t, long intlong long int 同等のタイプになります - これらのタイプを使用するときに生成されるアセンブリは同一です。一目見て stdint.h 理由を教えてください:

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

64ビットコンパイルで、 int64_tlong 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つだけです。 intlong もちろん、異なるタイプです。

回避策がないことを見るのは難しくありません 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*'
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top