问题描述
我试图编译一个简单的表达式,它可以归结为
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_t
在 int
运算符中被提升为 ?:
,而 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
的类型相同。