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.<cstdint>
, jadi mungkin fakta bahwa ia mengatakan "ini adalah ekstensi" (yang mana itu) adalah foobaring itu.--std=c++0x
. Dan yasizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
,.long
danlong long
merupakan tipe yang berbeda (meskipun mereka memiliki ukuran & representasi yang sama).int64_t
selalu merupakan alias untuk tipe lain yang sudah ada (terlepas dari namanya,typedef
tidak membuat tipe baru, ini hanya memberi alias untuk yang sudah ada)int16_t
, maka spesialisasikan denganshort
danint
dan Anda akan tercakup. (dan dengansigned char
jika Anda merasa suka berpetualang)Jawaban:
Anda tidak perlu pergi ke 64-bit untuk melihat sesuatu seperti ini. Pertimbangkan
int32_t
platform 32-bit umum. Ini mungkintypedef
'ed sebagaiint
atau sebagailong
, tapi jelas hanya satu dari dua pada satu waktu.int
danlong
tentu saja merupakan tipe yang berbeda.Tidak sulit untuk melihat bahwa tidak ada solusi yang dibuat
int == int32_t == long
pada sistem 32-bit. Untuk alasan yang sama, tidak ada cara untuk membuatlong == int64_t == long long
sistem 64-bit.Jika Anda bisa, kemungkinan konsekuensi akan agak menyakitkan untuk kode yang kelebihan beban
foo(int)
,foo(long)
danfoo(long long)
- tiba-tiba mereka memiliki dua definisi untuk kelebihan yang sama ?!Solusi yang benar adalah bahwa kode template Anda biasanya tidak bergantung pada jenis yang tepat, tetapi pada properti jenis tersebut. Seluruh
same_type
logika masih bisa OK untuk kasus tertentu:long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
Yaitu, kelebihan beban
foo(int64_t)
tidak didefinisikan ketika itu persis sama denganfoo(long)
.[sunting] Dengan C ++ 11, kami sekarang memiliki cara standar untuk menulis ini:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
[sunting] Atau C ++ 20
long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
sumber
sizeof()
long
danint
identik, tetapistd::is_same<long, int>::value
kembalifalse
. Keanehan yang sama di AppleClang 9.1 di OSX HighSierra.Apakah Anda ingin tahu apakah suatu tipe memiliki tipe yang sama dengan int64_t atau Anda ingin tahu apakah ada 64 bit? Berdasarkan solusi yang Anda usulkan, saya pikir Anda bertanya tentang yang terakhir. Kalau begitu, saya akan melakukan sesuatu seperti
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
sumber
return
dan titik koma?sizeof
untuk ini.short
= 16 bit,long
= 32 bit, danint
= ukuran asli. Di hari-hari 64-bit,int
danlong
tidak berarti apa - apa lagi.short
adalah setidaknya 16 bit,int
setidaknya 16 bit, danlong
setidaknya 32 bit, dengan (notasi ceroboh berikut) pendek <= int <= lama. "Masa lalu" yang Anda maksud tidak pernah ada; selalu ada variasi dalam batasan yang diberlakukan oleh bahasa. The "All the world an x86" fallacy sama berbahayanya dengan "All the world a VAX fallacy" yang lebih tua.Ini adalah pertanyaan atau masalah yang bagus, tapi saya rasa jawabannya TIDAK.
Juga, a
long int
mungkin bukanlong long int
.Saya yakin ini libc. Saya rasa Anda ingin masuk lebih dalam.
Linux 32-bit menggunakan model data ILP32. Integer, long dan pointer adalah 32-bit. Tipe 64-bit adalah a
long long
.Microsoft mendokumentasikan rentang di Rentang Tipe Data . Pepatah
long long
itu setara dengan__int64
.Linux 64-bit menggunakan
LP64
model data. Panjangnya 64-bit danlong long
64-bit. Seperti dengan 32-bit, Microsoft mendokumentasikan rentang pada Rentang Jenis Data dan masih panjang__int64
.Ada
ILP64
model data yang semuanya 64-bit. Anda harus melakukan pekerjaan ekstra untuk mendapatkan definisi untukword32
tipe Anda . Juga lihat makalah seperti Model Pemrograman 64-Bit: Mengapa LP64?Ya, itu menjadi lebih baik. GCC mencampur dan mencocokkan deklarasi yang seharusnya menggunakan tipe 64 bit, sehingga mudah mendapat masalah meskipun Anda mengikuti model data tertentu. Misalnya, berikut ini menyebabkan kesalahan kompilasi dan memberitahu Anda untuk menggunakan
-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);
Ini menghasilkan:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Jadi, abaikan
LP64
dan ubah menjadi:typedef unsigned long long word64;
Kemudian, jelajahi gadget ARM IoT 64-bit yang mendefinisikan
LP64
dan menggunakan NEON:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
sumber