Почему common_type & lt; long, unsigned long & gt; :: type = long long?

common_type<long, unsigned long>::type является unsigned long потому что относительно операндов после интегрального продвижения стандарт говорит …

[…] если операнд с целым типом без знака имеет ранг больше или
равен рангу типа другого операнда, операнд с
целочисленный тип со знаком должен быть преобразован в тип операнда с
целое число без знака

Не называть интегральную систему продвижения ошибочной, но кажется, что если существует целочисленный тип со знаком большего размера, который может представлять диапазон как операндов со знаком, так и без знака, его следует использовать.

Я знаю, что некоторые платформы могут иметь long == long long, и в этом случае вышеуказанное правило может вступить в силу. Но если там является доступен более крупный тип со знаком, не так ли?

19

Решение

Во-первых, 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;
}

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

-Ханнес

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]