Язык C++: длинный тип int и Long и int и int64 значение типа Т
я испытал некоторое странное поведение при использовании черт типа 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, а не long long int (очевидно).
исправление для этой ситуации довольно легко:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
но это ужасно hackish и не масштабируется хорошо (фактические функции вещества,uint64_t, etc). Итак, мой вопрос: есть ли способ сказать компилятору, что a long long int это также a int64_t, так как long int - это?
мои первоначальные мысли заключаются в том, что это невозможно из-за того, как работают определения типов C/C++. Не существует способа, чтобы указать эквивалентность типов исходных данных типы для компилятора, так как это работа компилятора (и позволяя это может сломать много вещей) и typedef идет только в одну сторону.
я также не слишком озабочен получением ответа здесь, так как это супер-пупер крайний случай, который я не подозреваю, что кто-то когда-либо будет заботиться о том, когда примеры не ужасно придуманы (означает ли это, что это должно быть сообщество wiki?).
добавить: причина, по которой я использую частичный шаблон специализация вместо более простого примера, как:
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 { };
In этот пример,some_type_trait<long int> будет boost::true_type, а some_type_trait<long long int> не будет. Хотя это имеет смысл в идее типов C++, это нежелательно.
другой пример - использование квалификатора типа same_type (что довольно часто используется в концепциях C++0x):
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&).
я хотел бы подчеркните, что я понимаю почему это происходит, но я ищу обходной путь, который не заставляет меня повторять код повсюду.
3 ответов:
вам не нужно идти в 64-бит, чтобы увидеть что-то вроде этого. Рассмотрим
int32_tна общих 32-разрядных платформах. Это может бытьtypedef' ed asintили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);
вы хотите знать, если тип такой же тип, как int64_t или вы хотите знать, если что-то 64 бит? Основываясь на вашем предлагаемом решении, я думаю, что вы спрашиваете о последнем. В таком случае, я бы сделал что-то вроде
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
Итак, мой вопрос: есть ли способ сказать компилятору, что long long int также является int64_t, как и long int?
это хороший вопрос или проблема, но я подозреваю, что ответа нет.
кроме того, a
long intне может бытьlong long int.
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endifя считаю, что это libc. Я подозреваю, что вы хотите пойти глубже.
как в 32-битной компиляции с GCC (и с 32-и 64-битный MSVC), вывод программы будет:
int: 0 int64_t: 1 long int: 0 long long int: 132-разрядная версия Linux использует модель данных ILP32. Целых чисел, длинных и указатели являются 32-разрядными. 64-битный тип это
long long.Microsoft документирует диапазоны в Диапазоны Типов Данных. То сказать то
long longэквивалентно__int64.однако программа, полученная в результате 64-разрядной компиляции GCC, выведет:
int: 0 int64_t: 1 long int: 1 long long int: 064-разрядный Linux использует элемент
LP64модель данных. Длинные 64-битные иlong long64-разрядные. Как и в случае с 32-разрядной версией, Microsoft документирует диапазоны в Диапазоны Типов Данных а долго-долго еще__int64.здесь
ILP64модель данных, где все 64-битные. Вы должны сделать некоторую дополнительную работу, чтобы получить определениеword32тип. Также см. Такие документы, как 64-разрядные модели программирования: почему LP64?
но это ужасно hackish и делает не масштабируется хорошо (фактические функции вещества, uint64_t и т. д.)...
Да, становится еще лучше. 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и использовать неон:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
Comments