Почему не common_type<long, unsigned long>:: type = long long?
common_type<long, unsigned long>::type
является unsigned long
потому что относительно операндов после интегрального продвижения стандарт говорит...
[...] если операнд с целым типом без знака имеет ранг, больший или равный рангу типа другого операнда, операнд с целым типом со знаком должен быть преобразован в тип операнда с целым типом без знака
Не называть интегральную систему продвижения ошибочной, но кажется, что если существует целочисленный тип со знаком большего размера, который может представлять диапазон как операндов со знаком, так и без знака, его следует использовать.
Я знаю, что некоторые платформы могут иметь long == long long, и в этом случае вышеуказанное правило может вступить в силу. Но если имеется более крупный тип со знаком, можно ли его использовать?
1 ответ
Прежде всего, std::common_type (и, конечно, boost::type_traits::common_type) используют троичный оператор для получения результата типа. В этом случае соответствующая цитата взята из CppReference, 6b)
E2 и E3 имеют арифметический или перечислимый тип: применяются обычные арифметические преобразования, чтобы привести их к общему типу, этот тип является результатом.
С помощью этой информации мы можем найти правила для обычных арифметических преобразований в стандарте C++, 5p10, стр. 88.
- В противном случае, если операнд с целочисленным типом без знака имеет ранг больше или равен рангу типа другого операнда, операнд с целочисленным типом со знаком должен быть преобразован в тип операнда с целочисленным типом без знака.
Таким образом, в основном ответ на ваш вопрос: ... потому что стандарт говорит так.
Но вы не единственный, кто находит такое поведение неожиданным. Вот быстрый исполняемый пример, чтобы попробовать:
#include <iostream>
#include <typeinfo>
#include <type_traits>
int main(int argc, const char* argv[])
{
std::cout << typeid(std::common_type<char, unsigned char>::type).name() << std::endl;
// I would expect "short", and the result is "int", ok so far.
std::cout << typeid(std::common_type<short, unsigned short>::type).name() << std::endl;
// I would expect "int", and the result is "int", yay.
std::cout << typeid(std::common_type<int, unsigned int>::type).name() << std::endl;
// I would expect "long", but the result is "unsigned int"
std::cout << typeid(std::common_type<long, unsigned long>::type).name() << std::endl;
// I would expect "long long", but the result is "unsigned long"
// So this usual arithmetic conversion can lead to unexpected behavior:
auto var_auto = true ? var_i : var_ui;
std::cout << typeid(var_auto).name() << std::endl; // unsigned int
std::cout << var_auto << std::endl; // 4294967173
return 0;
}
Но то, что текущее поведение является проблемой, известно, и существует предложение убрать некоторые сюрпризы.
-Hannes