Я испытал странное поведение при использовании черт типа C ++ и сузил свою проблему до этой причудливой маленькой проблемы, для которой я дам множество объяснений, поскольку я не хочу оставлять что-либо открытым для неправильной интерпретации.
Допустим, у вас есть такая программа:
#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;
}
В обоих 32-битных компиляторах с GCC (а также с 32- и 64-битными MSVC) вывод программы будет:
int: 0
int64_t: 1
long int: 0
long long int: 1
Однако программа, полученная в результате компиляции 64-битного GCC, выведет:
int: 0
int64_t: 1
long int: 1
long long int: 0
Это любопытно, так как long long int
это знаковое 64-битовое целое число , и, для всех намерений и целей, идентична long int
и int64_t
типов, так логично, int64_t
, long int
и long long int
было бы эквивалентные типы - сборка генерируется при использовании этих типов идентичны. Один взгляд stdint.h
говорит мне, почему:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
В 64-битной компиляции int64_t
это long int
не a long long int
(очевидно).
Исправить эту ситуацию довольно просто:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Но это ужасно хакерское решение и плохо масштабируется (фактические функции вещества uint64_t
и т. Д.). Итак, мой вопрос: есть ли способ сообщить компилятору, что a long long int
также a int64_t
, как и long int
есть?
Мои первоначальные мысли заключаются в том, что это невозможно из-за того, как работают определения типов C / C ++. Невозможно указать компилятору эквивалентность основных типов данных, поскольку это задача компилятора (и разрешение этого может нарушить многие вещи) и действует typedef
только в одном направлении.
Я также не слишком беспокоюсь о том, чтобы получить здесь ответ, поскольку это супер-пупер крайний случай, который, я не подозреваю, кого-то когда-либо будет волновать, когда примеры не будут ужасно надуманными (означает ли это, что это должна быть вики сообщества?) .
Приложение : причина, по которой я использую частичную специализацию шаблона вместо более простого примера, такого как:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
этот пример все равно будет компилироваться, поскольку long long int
он неявно конвертируется в int64_t
.
Приложение : единственный ответ на данный момент предполагает, что я хочу знать, является ли тип 64-битным. Я не хотел вводить людей в заблуждение, заставляя думать, что я забочусь об этом, и, вероятно, должен был привести больше примеров того, где эта проблема проявляется.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
В этом примере some_type_trait<long int>
будет boost::true_type
, но some_type_trait<long long int>
не будет. Хотя это имеет смысл в представлении C ++ о типах, это нежелательно.
Другой пример - использование такого квалификатора same_type
(который довольно часто используется в C ++ 0x Concepts):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Этот пример не компилируется, поскольку C ++ (правильно) видит, что типы разные. g ++ не сможет скомпилировать с ошибкой, например: нет соответствующего вызова функции same_type(long int&, long long int&)
.
Я хотел бы подчеркнуть, что понимаю, почему это происходит, но я ищу обходной путь, который не заставит меня повторять код повсюду.
sizeof
каждого типа? Возможно, компилятор по-long long int
другому трактует размер .<cstdint>
, так что, возможно, тот факт, что он должен сказать «это расширение» (а это так), обманывает его.--std=c++0x
. И да,sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
.long
иlong long
являются разными типами (даже если они имеют одинаковый размер и представление).int64_t
всегда является псевдонимом для другого существующего типа (несмотря на свое название,typedef
не создает новые типы, он просто дает псевдоним для уже существующего)int16_t
, специализируйтесь наshort
и,int
и вы будете защищены. (иsigned char
если вы любите приключения)Ответы:
Вам не нужно переходить на 64-битную версию, чтобы увидеть что-то подобное. Рассмотрим
int32_t
на распространенных 32-битных платформах. Это может бытьtypedef
какint
или какlong
, но, очевидно, только одно из двух одновременно.int
иlong
, конечно, разные типы.Нетрудно заметить, что не существует обходного пути, который работает
int == int32_t == long
в 32-битных системах. По той же причине нет возможности делать этоlong == int64_t == long long
в 64-битных системах.Если бы вы могли, возможные последствия были бы довольно болезненными для кода, который перегружен
foo(int)
,foo(long)
иfoo(long long)
- вдруг у них было бы два определения для одной и той же перегрузки ?!Правильное решение состоит в том, что код вашего шаблона обычно должен полагаться не на конкретный тип, а на свойства этого типа. Вся
same_type
логика все еще может быть в порядке для конкретных случаев:long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
Т.е. перегрузка
foo(int64_t)
не определяется, когда она точно такая же какfoo(long)
.[править] В C ++ 11 теперь есть стандартный способ написать это:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
[править] Или C ++ 20
long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
источник
sizeof()
long
иint
идентичны, ноstd::is_same<long, int>::value
возвращаютсяfalse
. Та же странность на AppleClang 9.1 на OSX HighSierra.Вы хотите знать, является ли тип тем же типом, что и int64_t, или вы хотите знать, является ли что-то 64-битным? Основываясь на предложенном вами решении, я думаю, вы спрашиваете о последнем. В этом случае я бы сделал что-то вроде
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
источник
return
и точку с запятой?sizeof
для этого.short
16 бит,long
32 бита иint
собственный размер. В наши дни 64-битныеint
иlong
больше ничего не значат .short
составляет не менее 16 бит,int
не менее 16 бит иlong
не менее 32 бит, с (следует небрежная запись) short <= int <= long. «Старые дни», о которых вы говорите, никогда не существовали; всегда были различия в пределах ограничений, налагаемых языком. Заблуждение «Весь мир - x86» так же опасно, как и заблуждение «Весь мир - VAX».Это хороший вопрос или проблема, но я подозреваю, что ответ - НЕТ.
Кроме того, a
long int
может не бытьlong long int
.Я считаю, что это libc. Я подозреваю, что вы хотите глубже.
32-битный Linux использует модель данных ILP32. Целые числа, длинные числа и указатели 32-битные. 64-битный тип - это
long long
.Microsoft документирует диапазоны как диапазоны типов данных . Сказать
long long
эквивалентно__int64
.64-битный Linux использует
LP64
модель данных. Long 64-битные иlong long
64-битные. Как и в случае с 32-разрядной версией, Microsoft документирует диапазоны в Data Type Ranges, а long long по-прежнему остается__int64
.Есть
ILP64
модель данных, в которой все 64-битное. Вы должны проделать дополнительную работу, чтобы получить определение вашегоword32
типа. Также см. Статьи, такие как 64-битные модели программирования: почему именно LP64?Да, становится еще лучше. GCC смешивает и сопоставляет объявления, которые должны принимать 64-битные типы, поэтому легко попасть в неприятности, даже если вы следуете определенной модели данных. Например, следующее вызывает ошибку компиляции и предлагает использовать
-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);
Это приводит к:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Итак, игнорируйте
LP64
и измените его на:typedef unsigned long long word64;
Затем перейдите к 64-битному гаджету ARM IoT, который определяет
LP64
и использует NEON:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
источник