数字文字到无符号类型的类型推导

问题描述

我试图编译一个简单的表达式,它可以归结为

myClass* object = new myClass();
std::vector<someType> SomeContainer;
auto N = (object) ? object->GetN() : 0; //GetN() returns <uint16_t>
assert( SomeContainer.size() == N ); // error,comparing signed to unsigned

我知道这会发生,因为

rank(uint16_t)=rank(unsigned short int)<rank(int)=rank(0)

这样 uint16_tint 运算符中被提升为 ?:,而 N 也变成了 int。因此,我可以通过以下任一行来解决这个问题

auto N = (object) ? object->GetN() : 0u; //promotion of <uint16_t> to <uint>,N is <uint>
auto N = (object) ? object->GetN() : static_cast<uint16_t>(0); //no promotion,N is <uint16_t>
auto N = (object) ? static_cast<size_t>(object->GetN()) : 0; //promotion of <uint16_t> to <size_t>,N is <size_t>

因此,要么我对其中一个参数使用“太大”类型,要么我必须在 0 上使用 static_cast,这对我来说似乎不太优雅。我真的不明白,为什么文字 0 的处理是违反直觉的,在这种情况下它真的破坏了 auto用法

您不应该期望 N 具有无符号类型(甚至特别是 GetN() 的类型)吗?应该首选哪种解决方案,还是没有优雅的解决方案?


值得注意的是,使 N const 没有帮助,因为它会在:

size_t a=0;
auto N=0;
const auto k_N=0;
assert(a==N); // error,comparing signed to unsigned
assert(a==k_N); // true;

所以不应该

const auto N = (object) ? object->GetN() : 0;

也会导致无符号类型,因为 N 从不为负数?


编辑:编译器是 GCC 7.3.1。仅在调试版本中发生。

解决方法

如果你想在现在和将来避免任何不需要的类型转换,那么你可以强制三元条件运算符自己表现

object ? object->GetN() : static_cast<decltype(object->GetN())>(0)

虽然啰嗦,但你至少没有对类型进行硬编码,并且这个表达式的类型将是 object->GetN() 的类型。 (请注意,object->GetN() 未在 decltype 中求值 - 此处没有未定义的行为。)

请注意,0 始终是 int 类型的八进制文字。在具有 16 位 int 的平台上,它会被提升为 unsigned int;可能(虽然不能保证)与 std::uint16_t 的类型相同。