Вопрос

Я испытал какое-то странное поведение при использовании типовых черт 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

Но это ужасно взлома и хорошо масштабируется (фактические функции вещества, uint64_t, так далее). Итак, мой вопрос: Есть ли способ сказать компилятору, что long long int это также 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 { };

В этом примере, 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&).

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

Это было полезно?

Решение

Вам не нужно идти на 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);

Другие советы

Вы хотите знать, является ли тип тот же тип, что и INT64_T или вы хотите знать, если что-то 64 бита? На основании вашего предлагаемого решения, я думаю, вы спрашиваете о последнем. В этом случае я бы сделал что-то вроде

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

Итак, мой вопрос: есть ли способ сказать компилятору, что давно длинный int - это также INT64_T, как долго INT?

Это хороший вопрос или проблема, но я подозреваю, что ответ нет.

Также 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?


Но это ужасно взлома и не масштабируется (фактические функции вещества, UINT64_T и т. Д.) ...

Да, это становится еще лучше. GCC Mixes и соответствия объявлениям, которые должны принимать 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*'
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top