Язык 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&).



я хотел бы подчеркните, что я понимаю почему это происходит, но я ищу обходной путь, который не заставляет меня повторять код повсюду.

716   3  

3 ответов:

вам не нужно идти в 64-бит, чтобы увидеть что-то вроде этого. Рассмотрим int32_t на общих 32-разрядных платформах. Это может быть typedef ' ed as 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);

вы хотите знать, если тип такой же тип, как 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: 1

32-разрядная версия Linux использует модель данных ILP32. Целых чисел, длинных и указатели являются 32-разрядными. 64-битный тип это long long.

Microsoft документирует диапазоны в Диапазоны Типов Данных. То сказать то long long эквивалентно __int64.

однако программа, полученная в результате 64-разрядной компиляции GCC, выведет:

int:           0
int64_t:       1
long int:      1
long long int: 0

64-разрядный Linux использует элемент LP64 модель данных. Длинные 64-битные и long long 64-разрядные. Как и в случае с 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

    Ничего не найдено.