Saya mengalami beberapa perilaku aneh saat menggunakan ciri tipe C ++ dan telah mempersempit masalah saya ke masalah kecil yang unik ini yang akan saya berikan banyak penjelasan karena saya tidak ingin membiarkan sesuatu terbuka untuk salah tafsir.
Katakanlah Anda memiliki program seperti ini:
#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;
}
Dalam kompilasi 32-bit dengan GCC (dan dengan MSVC 32- dan 64-bit), output dari program ini adalah:
int: 0
int64_t: 1
long int: 0
long long int: 1
Namun, program yang dihasilkan dari kompilasi GCC 64-bit akan menampilkan:
int: 0
int64_t: 1
long int: 1
long long int: 0
Ini aneh, karena long long int
merupakan integer 64-bit bertanda dan, untuk semua maksud dan tujuan, identik dengan tipe long int
dan int64_t
, jadi secara logis int64_t
, long int
dan long long int
akan menjadi tipe yang setara - rakitan yang dihasilkan saat menggunakan tipe ini identik. Satu tampilan stdint.h
memberi tahu saya mengapa:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
Dalam kompilasi 64-bit, int64_t
is long int
, bukan long long int
(jelas).
Perbaikan untuk situasi ini cukup mudah:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Tapi ini sangat hackish dan tidak berskala dengan baik (fungsi substansi yang sebenarnya uint64_t
, dll). Jadi pertanyaan saya adalah: Apakah ada cara untuk memberi tahu kompiler bahwa a long long int
juga a int64_t
, sama seperti long int
itu?
Pikiran awal saya adalah bahwa ini tidak mungkin, karena cara kerja definisi tipe C / C ++. Tidak ada cara untuk menentukan kesetaraan tipe dari tipe data dasar ke kompilator, karena itu adalah tugas kompiler (dan mengizinkannya dapat merusak banyak hal) dan typedef
hanya berjalan satu arah.
Saya juga tidak terlalu peduli untuk mendapatkan jawaban di sini, karena ini adalah kasus tepi super-duper yang saya tidak curiga akan ada yang peduli ketika contoh tidak dibuat-buat (apakah itu berarti ini harus wiki komunitas?) .
Tambahkan : Alasan mengapa saya menggunakan spesialisasi template parsial daripada contoh yang lebih mudah seperti:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
adalah bahwa contoh tersebut akan tetap dikompilasi, karena long long int
secara implisit dapat diubah menjadi file int64_t
.
Lampirkan : Satu-satunya jawaban sejauh ini mengasumsikan bahwa saya ingin tahu apakah suatu jenis adalah 64-bit. Saya tidak ingin menyesatkan orang agar berpikir bahwa saya peduli tentang itu dan mungkin seharusnya memberikan lebih banyak contoh di mana masalah ini terwujud.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
Dalam contoh ini, some_type_trait<long int>
akan menjadi boost::true_type
, tetapi some_type_trait<long long int>
tidak akan. Meskipun ini masuk akal dalam gagasan tipe C ++, ini tidak diinginkan.
Contoh lain adalah menggunakan qualifier seperti same_type
(yang cukup umum digunakan dalam Konsep C ++ 0x):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Contoh itu gagal untuk dikompilasi, karena C ++ (dengan benar) melihat bahwa tipenya berbeda. g ++ akan gagal untuk dikompilasi dengan kesalahan seperti: tidak ada panggilan fungsi yang cocok same_type(long int&, long long int&)
.
Saya ingin menekankan bahwa saya memahami mengapa hal ini terjadi, tetapi saya mencari solusi yang tidak memaksa saya untuk mengulangi kode di semua tempat.
sizeof
setiap jenis? Mungkin kompilator memperlakukan ukuran secaralong long int
berbeda.