long long int - long int - C ++ 'da int64_t


87

C ++ tipi özellikleri kullanırken bazı garip davranışlar yaşadım ve sorunumu, yanlış yorumlamaya açık bir şey bırakmak istemediğim için bir ton açıklama yapacağım bu ilginç küçük soruna indirgedim.

Şöyle bir programınız olduğunu varsayalım:

#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 ile hem 32-bit derlemede (hem de 32- ve 64-bit MSVC ile), programın çıktısı şöyle olacaktır:

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

Ancak, 64 bitlik bir GCC derlemesinden kaynaklanan program şu çıktıyı verecektir:

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

Çünkü bu, meraklı long long intimzalı 64 bitlik tamsayı olup özdeş tüm niyet ve amaçlar için olduğunu long intve int64_ttürleri, bu nedenle mantıklı int64_t, long intve long long intbu tür özdeş olduğu kullanırken montaj üretilen - eşdeğer tipleri olurdu. Bir bakış stdint.hbana nedenini anlatıyor:

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

64-bit derleme olarak, int64_tolduğu long intdeğil, long long int(tabii ki).

Bu durumun düzeltilmesi oldukça kolaydır:

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

Ancak bu korkunç derecede hilekar ve iyi ölçeklenmiyor (maddenin gerçek işlevleri uint64_t, vb.). Yani benim soru: derleyici anlamanın bir yolu var mı bir o long long intaynı zamanda bir olan int64_ttıpkı long intmi?


İlk düşüncelerim, C / C ++ tür tanımlarının çalışma şekli nedeniyle bunun mümkün olmadığıdır. Derleyiciye temel veri türlerinin tür eşdeğerliğini belirlemenin bir yolu yoktur, çünkü bu derleyicinin işidir (ve buna izin vermek birçok şeyi bozabilir) ve typedefyalnızca tek yönlüdür.

Ayrıca burada bir cevap almakla da ilgilenmiyorum, çünkü bu, örnekler korkunç derecede uydurulmadığında kimsenin umursayacağından şüphelenmediğim süper uç bir durum (bu, bunun topluluk wiki olması gerektiği anlamına mı geliyor?) .


Ek : Aşağıdaki gibi daha kolay bir örnek yerine kısmi şablon uzmanlığı kullanmamın nedeni:

void go(int64_t) { }

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

söz konusu örneğin long long intörtük olarak bir int64_t.


Ek : Şimdiye kadarki tek cevap, bir türün 64 bit olup olmadığını bilmek istediğimi varsayıyor. İnsanları yanıltarak bunu önemsediğimi düşünmek istemedim ve muhtemelen bu sorunun kendini gösterdiği yere dair daha fazla örnek vermeliydim.

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

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

Bu örnekte, some_type_trait<long int>olacak boost::true_type, ancak some_type_trait<long long int>olmayacak. Bu, C ++ 'ın türler fikrinde mantıklı olsa da arzu edilen bir şey değildir.

Başka bir örnek, same_type(C ++ 0x Kavramlarında oldukça yaygın olan) gibi bir niteleyici kullanmaktır:

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

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

C ++ (doğru şekilde) türlerin farklı olduğunu gördüğünden, bu örnek derlenemez. g ++ aşağıdaki gibi bir hatayla derlenemez: eşleşen işlev çağrısı yok same_type(long int&, long long int&).

Bunun neden olduğunu anladığımı vurgulamak isterim , ancak beni her yerde kodu tekrar etmeye zorlamayan bir geçici çözüm arıyorum.


Merak ediyorum, örnek programınız sizeofher tür için aynı sonuçları veriyor mu? Belki de derleyici boyutuna long long intfarklı davranıyor.
Blair Holloway

C ++ 0x etkin olarak derlediniz mi? C ++ 03'e sahip değil <cstdint>, bu yüzden belki de "bu bir uzantıdır" (ki öyle) demesi gerektiği gerçeği onu kandırıyor.
GManNickG

Evet, muhtemelen kullandığımı belirtmeliydim --std=c++0x. Ve evet sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8,.
Travis Gockel

1
Henüz kimse bundan bahsetmedi, ancak göz ardı edilmiş olması durumunda: longve long longfarklı türlerdir (aynı boyut ve temsile sahip olsalar bile). int64_ther zaman başka bir mevcut tür için bir takma addır (ismine rağmen, typedefyeni türler oluşturmaz, sadece zaten var
MM

3
Cevaplarda / yorumlarda önemli bir ifade eksik, bu tuhaflık beni etkilediğinde bana yardımcı oldu: Güvenilir bir şekilde uzmanlaşan şablonlar için asla sabit boyutlu türleri kullanmayın. Her zaman temel türleri kullanın ve olası tüm durumları kapatın (bu şablonları başlatmak için sabit boyutlu türler kullansanız bile). Tüm olası durumlar şu anlama gelir: ile örneklemeniz gerekiyorsa int16_t, o zaman uzmanlaşın shortve intkapsanacaksınız. (ve signed charmaceracı hissediyorsanız)
Irfy

Yanıtlar:


49

Böyle bir şey görmek için 64-bite gitmenize gerek yok. int32_tYaygın 32 bit platformları düşünün . typedefOlarak intveya olarak verilebilir long, ancak belli ki bir seferde ikisinden sadece biri. intve longelbette farklı türlerdir.

int == int32_t == long32 bit sistemlerde yapan bir çözüm olmadığını görmek zor değil . Aynı nedenle, long == int64_t == long long64 bit sistemlerde yapmanın bir yolu yoktur .

Eğer yapabilseydim, olası sonuçlarıyla aşırı o kodu için oldukça acı verici olurdu foo(int), foo(long)ve foo(long long)- aniden aynı aşırı yük için iki tanım olurdu ?!

Doğru çözüm, şablon kodunuzun genellikle kesin bir türe değil, o türün özelliklerine dayanması gerektiğidir. same_typeBelirli durumlar için tüm mantık yine de uygun olabilir:

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

Yani, aşırı yük tam olarak aynı foo(int64_t)olduğunda tanımlanmaz .foo(long)

[değiştir] C ++ 11 ile artık bunu yazmak için standart bir yolumuz var:

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

[düzenle] Veya C ++ 20

long foo(long x);
int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);

1
Üzücü haber, örneğin 64bit MSVC19 (2017) 'de sizeof() longve intaynı, ancak std::is_same<long, int>::valuegeri dönüyor false. OSX HighSierra'da AppleClang 9.1'de aynı tuhaflık.
Ax3l

3
@ Ax3l: Bu tuhaf değil. ISO C 90'dan bu yana neredeyse her derleyicide böyle en az bir çift bulunur.
MSalters

Bu doğru, farklı tipler.
Ax3l

6

Bir türün int64_t ile aynı tür olup olmadığını mı yoksa 64 bit mi olduğunu bilmek ister misiniz? Önerilen çözümünüze dayanarak, ikincisini sorduğunuzu düşünüyorum. Bu durumda, şöyle bir şey yapardım

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

1
Bir returnve noktalı virgül eksik değil mi ?
casablanca

1
Yine de bunun için kullanmalısınız sizeof.
Ben Voigt

5
long long int ve long int aynı boyutta olsun ya da olmasın aynı tip değildir. Davranış hatalı değil. Bu sadece C ++.
Logan Capaldo

5
Bu, nominal yazmanın bir sınırlaması değildir. Anlamsız nominal yazımın bir sınırlaması . Eski günlerde fiili standart short= 16 bit, long= 32 bit ve int= yerel boyuttu. 64-bit bu günlerde, içinde intve longyok demek , artık hiçbir şey.
dan04

1
@ dan04: Hiç olmadığı kadar anlamlı değiller. shortolan en az bir , 16 bit inten az 16 bittir ve longkısa <= int <= uzun (özensiz gösterimde aşağıdaki) ile, en az 32 bittir. Hiç var olmadı dediğiniz "eski günler"; dilin koyduğu kısıtlamalar içinde her zaman farklılıklar olmuştur. "Tüm dünyanın bir x86" yanılgısı, en az eski "tüm dünya bir VAX yanlışlığı kadar tehlikelidir.
Keith Thompson

1

Öyleyse sorum şu: Derleyiciye, long int gibi, long long int de int64_t olduğunu söylemenin bir yolu var mı?

Bu iyi bir soru veya problem, ancak cevabın HAYIR olduğundan şüpheleniyorum.

Ayrıca, a, a long intolmayabilir long long int.


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

Bunun libc olduğuna inanıyorum. Daha derine inmek istediğinden şüpheleniyorum.

GCC ile hem 32-bit derlemede (hem de 32- ve 64-bit MSVC ile), programın çıktısı şöyle olacaktır:

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

32 bit Linux, ILP32 veri modelini kullanır. Tam sayılar, uzunlar ve işaretçiler 32 bittir. 64 bit türü bir long long.

Microsoft, aralıkları Veri Türü Aralıklarında belgelemektedir . Demek long longeşdeğerdir __int64.

Ancak, 64 bitlik bir GCC derlemesinden kaynaklanan program şu çıktıyı verecektir:

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

64 bit Linux, LP64veri modelini kullanır . Uzun parçalar 64 bit ve long long64 bittir. 32-bit ile olduğu gibi, Microsoft Veri Tipi Aralıkları'ndaki aralıkları belgelemektedir ve uzun hala devam etmektedir __int64.

Her ILP64şeyin 64 bit olduğu bir veri modeli var. Türünüzün tanımını almak için fazladan çalışma yapmanız gerekir word32. Ayrıca 64-Bit Programlama Modelleri gibi kağıtlara da bakın : Neden LP64?


Ancak bu korkunç derecede hack'lidir ve iyi ölçeklenmez (maddenin gerçek işlevleri, uint64_t, vb.) ...

Evet, daha da iyi oluyor. GCC, 64 bit türleri alması gereken bildirimleri karıştırır ve eşleştirir, böylece belirli bir veri modelini takip etseniz bile sorun çıkarmak kolaydır. Örneğin, aşağıdaki bir derleme hatasına neden olur ve size şunu kullanmanızı söyler -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);

Sonuç:

error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

Bu nedenle, yok sayın LP64ve şu şekilde değiştirin:

typedef unsigned long long word64;

Ardından NEON'u tanımlayan LP64ve kullanan 64 bitlik bir ARM IoT cihazına gidin :

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.