C ++: long long int vs. long int vs. int64_t
Eu experimentei um comportamento estranho ao usar traços do tipo C ++ e reduzi meu problema a este pequeno problema peculiar, sobre o qual darei uma tonelada de explicações, pois não quero deixar nada em aberto para interpretações errôneas.
Digamos que você tenha um programa assim:
#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;
}
Na compilação de 32 bits com o GCC (e no MSVC de 32 e 64 bits), a saída do programa será:
int: 0
int64_t: 1
long int: 0
long long int: 1
No entanto, o programa resultante de uma compilação do GCC de 64 bits produzirá:
int: 0
int64_t: 1
long int: 1
long long int: 0
Isso é curioso, já quelong long int
é um número inteiro de 64 bits assinado e é, para todos os efeitos, idêntico aolong int
eint64_t
tipos, então logicamente,int64_t
, long int
elong long int
seriam tipos equivalentes - a montagem gerada ao usar esses tipos é idêntica. Uma olhadastdint.h
me diz o porquê:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
Em uma compilação de 64 bits,int64_t
élong int
, não umlong long int
(obviamente).
A correção para essa situação é bastante fácil:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Mas isso é terrivelmente hackish e não escala bem (funções reais da substância,uint64_t
etc).Então, minha pergunta é: Existe uma maneira de dizer ao compilador que umlong long int
é também oint64_t
, Assim comolong int
é?
Meu pensamento inicial é que isso não é possível, devido à maneira como as definições de tipo C / C ++ funcionam. Não existe uma maneira de especificar a equivalência de tipo dos tipos de dados básicos para o compilador, pois esse é o trabalho do compilador (e permitir que isso possa quebrar muitas coisas) etypedef
só vai de um jeito.
Também não estou muito preocupado em obter uma resposta aqui, já que este é um caso de super-duper que eu não suspeito que alguém se importe quando os exemplos não forem terrivelmente inventados (isso significa que esse deve ser um wiki da comunidade?) .
Acrescentar: A razão pela qual estou usando a especialização parcial de modelos em vez de um exemplo mais fácil, como:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
é que esse exemplo ainda será compilado, poislong long int
é implicitamente conversível em umint64_t
.
Acrescentar: A única resposta até agora pressupõe que eu queira saber se um tipo é de 64 bits. Eu não queria induzir as pessoas a pensar que me importo com isso e provavelmente deveria ter fornecido mais exemplos de onde esse problema se manifesta.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
Neste exemplo,some_type_trait<long int>
será umaboost::true_type
, massome_type_trait<long long int>
não será. Embora isso faça sentido na idéia de tipos do C ++, não é desejável.
Outro exemplo é usar um qualificador comosame_type
(que é bastante comum de usar nos conceitos do C ++ 0x):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Esse exemplo falha na compilação, pois o C ++ (corretamente) vê que os tipos são diferentes. O g ++ falhará ao compilar com um erro como: nenhuma chamada de função correspondentesame_type(long int&, long long int&)
.
Eu gostaria de enfatizar que eu entendoporque isso está acontecendo, mas estou procurando uma solução alternativa que não me force a repetir o código em todo o lugar.