Eu experimentei um comportamento estranho ao usar características do tipo C ++ e reduzi meu problema a este pequeno problema peculiar para o qual darei muitas explicações, já que não quero deixar nada aberto para interpretações erradas.
Digamos que você tenha um programa como este:
#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 GCC (e com 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 GCC de 64 bits produzirá:
int: 0
int64_t: 1
long int: 1
long long int: 0
Isso é curioso, já que long long int
é um inteiro assinado de 64 bits e é, para todos os efeitos, idêntico aos tipos long int
e int64_t
, então logicamente int64_t
, long int
e long long int
seriam tipos equivalentes - o assembly gerado ao usar esses tipos é idêntico. Uma olhada stdint.h
me diz por quê:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
Em uma 64 bits de compilação, int64_t
é long int
, não um long long int
(obviamente).
A solução para essa situação é muito fácil:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Mas isso é terrivelmente hackeado e não se ajusta bem (funções reais da substância uint64_t
, etc.). Portanto, minha pergunta é: há uma maneira de dizer ao compilador que long long int
a também é a int64_t
, assim como long int
é?
Meu pensamento inicial é que isso não é possível, devido à maneira como as definições de tipo C / C ++ funcionam. Não há uma maneira de especificar a equivalência de tipo dos tipos de dados básicos para o compilador, uma vez que essa é a função do compilador (e permitir isso poderia quebrar muitas coisas) e typedef
só vai em um caminho.
Eu também não estou muito preocupado em obter uma resposta aqui, uma vez que este é um caso extremamente extremo que eu não suspeito que alguém se importará quando os exemplos não forem terrivelmente inventados (isso significa que deve ser um wiki da comunidade?) .
Anexo : O motivo pelo qual estou usando a especialização parcial de modelo 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 o referido exemplo ainda será compilado, uma vez que long long int
é implicitamente conversível em um int64_t
.
Anexo : A única resposta até agora pressupõe que eu quero saber se um tipo é 64 bits. Não queria enganar as pessoas fazendo-as 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á um boost::true_type
, mas some_type_trait<long long int>
não será. Embora isso faça sentido na ideia de tipos do C ++, não é desejável.
Outro exemplo é usar um qualificador como same_type
(que é muito comum de usar em C ++ 0x Concepts):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Esse exemplo falha ao compilar, uma vez que C ++ (corretamente) vê que os tipos são diferentes. g ++ falhará ao compilar com um erro como: nenhuma chamada de função correspondente same_type(long int&, long long int&)
.
Gostaria de enfatizar que entendo por que isso está acontecendo, mas estou procurando uma solução que não me obrigue a repetir o código em todos os lugares.
sizeof
cada tipo? Talvez o compilador esteja tratando o tamanho de maneiralong long int
diferente.<cstdint>
, então talvez o fato de ter que dizer "esta é uma extensão" (o que é) seja um exagero.--std=c++0x
. E simsizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
,.long
elong long
são tipos distintos (mesmo que tenham o mesmo tamanho e representação).int64_t
é sempre um apelido para outro tipo existente (apesar do nome,typedef
não cria novos tipos, apenas dá um apelido para um que já existe)int16_t
, especialize-se comshort
eint
e você estará coberto. (esigned char
se você estiver se sentindo aventureiro)Respostas:
Você não precisa ir para 64 bits para ver algo assim. Considere
int32_t
em plataformas comuns de 32 bits. Pode sertypedef
ed comoint
ou como umlong
, mas obviamente apenas um dos dois de cada vez.int
e,long
claro, são tipos distintos.Não é difícil ver que não há uma solução alternativa
int == int32_t == long
para sistemas de 32 bits. Pelo mesmo motivo, não há como fazerlong == int64_t == long long
em sistemas de 64 bits.Se pudesse, as possíveis consequências seriam bastante doloroso para o código que sobrecarregados
foo(int)
,foo(long)
efoo(long long)
- de repente eles têm duas definições para a mesma sobrecarga ?!A solução correta é que o código do modelo geralmente não deve se basear em um tipo preciso, mas nas propriedades desse tipo. Toda a
same_type
lógica ainda pode estar OK para casos específicos:long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
Ou seja, a sobrecarga
foo(int64_t)
não é definida quando é exatamente igual afoo(long)
.[editar] Com C ++ 11, agora temos uma maneira padrão de escrever isso:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
[editar] Ou C ++ 20
long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
fonte
sizeof()
long
eint
é idêntica, masstd::is_same<long, int>::value
retornafalse
. Mesma estranheza no AppleClang 9.1 no OSX HighSierra.Você quer saber se um tipo é do mesmo tipo que int64_t ou quer saber se algo tem 64 bits? Com base em sua solução proposta, acho que você está perguntando sobre a última. Nesse caso, eu faria algo como
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
fonte
return
e um ponto e vírgula?sizeof
para isso.short
= 16 bits,long
= 32 bits eint
= tamanho nativo. Nestes dias de 64 bits,int
elong
não significam mais nada.short
tem pelo menos 16 bits,int
tem pelo menos 16 bits elong
tem pelo menos 32 bits, com (segue uma notação descuidada) curto <= int <= longo. Os "velhos tempos" aos quais você se refere nunca existiram; sempre houve variações dentro das restrições impostas pelo idioma. A falácia "Todo o mundo é um x86" é tão perigosa quanto a falácia "Todo o mundo é uma falácia VAX.Esta é uma boa pergunta ou problema, mas suspeito que a resposta seja NÃO.
Além disso, a
long int
pode não ser along long int
.Eu acredito que isso é libc. Eu suspeito que você queira ir mais fundo.
O Linux de 32 bits usa o modelo de dados ILP32. Inteiros, longos e ponteiros são de 32 bits. O tipo de 64 bits é a
long long
.A Microsoft documenta os intervalos em Intervalos de tipo de dados . A palavra que
long long
é equivalente a__int64
.Linux de 64 bits usa o
LP64
modelo de dados. Longs são de 64 bits elong long
64 bits. Tal como acontece com 32 bits, a Microsoft documenta os intervalos em Intervalos de tipo de dados e longo, longo ainda está__int64
.Existe um
ILP64
modelo de dados onde tudo é 64 bits. Você precisa fazer algum trabalho extra para obter uma definição para o seuword32
tipo. Veja também artigos como Modelos de programação de 64 bits: Por que LP64?Sim, fica ainda melhor. O GCC combina e combina declarações que supostamente aceitam tipos de 64 bits, então é fácil ter problemas mesmo que você siga um modelo de dados específico. Por exemplo, o seguinte causa um erro de compilação e instrui você a usar
-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);
Isso resulta em:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Portanto, ignore
LP64
e mude para:typedef unsigned long long word64;
Em seguida, vá até um gadget ARM IoT de 64 bits que define
LP64
e usa NEON:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
fonte