Doświadczyłem dziwnego zachowania podczas używania cech typu C ++ i zawęziłem swój problem do tego dziwacznego małego problemu, dla którego dam mnóstwo wyjaśnień, ponieważ nie chcę zostawiać niczego otwartego na błędną interpretację.
Powiedzmy, że masz taki program:
#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;
}
W obu 32-bitowych kompilacjach z GCC (oraz z 32- i 64-bitowym MSVC), wyjście programu będzie:
int: 0
int64_t: 1
long int: 0
long long int: 1
Jednak program powstały z 64-bitowej kompilacji GCC wyświetli:
int: 0
int64_t: 1
long int: 1
long long int: 0
Jest to ciekawe, ponieważ long long int
jest to 64-bitowa liczba całkowita ze znakiem i pod każdym względem jest identyczna z typami long int
i int64_t
, więc logicznie int64_t
, long int
i long long int
byłyby równoważnymi typami - zestaw generowany podczas korzystania z tych typów jest identyczny. Jedno spojrzenie stdint.h
mówi mi, dlaczego:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
W 64-bitowym kompilacji, int64_t
to long int
, a nie long long int
(oczywiście).
Rozwiązanie tej sytuacji jest dość łatwe:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Ale to jest strasznie hakerskie i nie skaluje się dobrze (rzeczywiste funkcje substancji uint64_t
itp.). Więc moje pytanie brzmi: czy istnieje sposób, aby powiedzieć kompilatorowi, że a long long int
jest również a int64_t
, tak jak long int
jest?
Moje początkowe przemyślenia są takie, że nie jest to możliwe ze względu na sposób działania definicji typów C / C ++. Nie ma sposobu, aby określić równoważność typów podstawowych typów danych kompilatorowi, ponieważ jest to zadanie kompilatora (i pozwolenie na to może zepsuć wiele rzeczy) i działa typedef
tylko w jedną stronę.
Nie jestem też zbyt zainteresowany uzyskaniem odpowiedzi tutaj, ponieważ jest to super-duper edge przypadek, o którym nie podejrzewam, że ktoś kiedykolwiek będzie się przejmował, gdy przykłady nie są okropnie wymyślone (czy to oznacza, że powinno to być wiki społeczności?) .
Dołącz : powód, dla którego używam częściowej specjalizacji szablonu zamiast prostszego przykładu, takiego jak:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
jest to, że wspomniany przykład będzie nadal kompilowany, ponieważ long long int
jest niejawnie konwertowany na plik int64_t
.
Dołącz : jedyna jak dotąd odpowiedź zakłada, że chcę wiedzieć, czy typ jest 64-bitowy. Nie chciałem wprowadzać ludzi w błąd, aby myśleli, że mi na tym zależy i prawdopodobnie powinienem był podać więcej przykładów, gdzie ten problem się objawia.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
W tym przykładzie some_type_trait<long int>
będzie boost::true_type
, ale some_type_trait<long long int>
nie będzie. Chociaż ma to sens w koncepcji typów w C ++, nie jest pożądane.
Innym przykładem jest użycie kwalifikatora takiego jak same_type
(który jest dość powszechny w C ++ 0x Pojęcia):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Ten przykład nie można skompilować, ponieważ C ++ (poprawnie) widzi, że typy są różne. g ++ nie skompiluje się z błędem takim jak: brak dopasowania funkcji same_type(long int&, long long int&)
.
Chciałbym podkreślić, że rozumiem, dlaczego tak się dzieje, ale szukam obejścia, które nie zmusza mnie do powtarzania kodu w każdym miejscu.
sizeof
każdego typu? Być może kompilatorlong long int
inaczej traktuje rozmiar .