common_type<long, unsigned long>::type
является unsigned long
потому что относительно операндов после интегрального продвижения стандарт говорит …
[…] если операнд с целым типом без знака имеет ранг больше или
равен рангу типа другого операнда, операнд с
целочисленный тип со знаком должен быть преобразован в тип операнда с
целое число без знака
Не называть интегральную систему продвижения ошибочной, но кажется, что если существует целочисленный тип со знаком большего размера, который может представлять диапазон как операндов со знаком, так и без знака, его следует использовать.
Я знаю, что некоторые платформы могут иметь long == long long, и в этом случае вышеуказанное правило может вступить в силу. Но если там является доступен более крупный тип со знаком, не так ли?
Во-первых, std :: common_type (и, конечно, boost :: type_traits :: common_type) используют троичный оператор для получения результата типа. В этом случае соответствующая цитата исходит от CppReference, 6b)
E2 и E3 имеют арифметический или перечислимый тип: применяются обычные арифметические преобразования, чтобы привести их к общему типу, этот тип является результатом.
С помощью этой информации мы можем найти правила для обычные арифметические преобразования в стандарт с ++, 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;
}
Но то, что текущее поведение является проблемой известен, и предложение существует, чтобы удалить некоторые из сюрпризов.
-Ханнес
Других решений пока нет …