Я столкнулся с некоторым нечетным поведением при использовании свойств типа С++ и сузил проблему до этой причудливой маленькой проблемы, для которой я дам тонну объяснения, потому что я не хочу оставлять ничего доступного для неправильной интерпретации.
Скажите, что у вас есть такая программа:
#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
является также a int64_t
, как и long int
?
Мои первоначальные мысли заключаются в том, что это невозможно, из-за того, как работают определения типов 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>
не будет. Хотя это имеет смысл в идеях типов С++, это нежелательно.
В другом примере используется классификатор типа same_type
(который довольно часто используется в концепциях С++ 0x):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Этот пример не скомпилируется, поскольку С++ (правильно) видит, что типы разные. g++ не сможет скомпилироваться с ошибкой вроде: никакой вызов функции соответствия same_type(long int&, long long int&)
.
Я хотел бы подчеркнуть, что я понимаю, почему это происходит, но я ищу обходное решение, которое не заставляет меня повторять код повсюду.