当我想在不同的整数类型之间进行转换时,似乎最好的语法是使用boost::numeric_cast<>()
:
int y = 99999;
short x = boost::numeric_cast<short>(y); // will throw an exception if y is too large
我从未使用过那个;但是语法非常简单,所以一切都很好。
现在假设我想做一些更高级的事情:我希望它能够返回目标类型的最小值或最大值(饱和度),而不是抛出异常。我无法找到表达方式,但documentation表明它是可能的(可能使用RawConverter
政策)。我能想出的就是下面的丑陋:
short x = numeric_cast<short>(max(min(y, SHORT_MAX), SHORT_MIN);
那么如何使用boost numeric_cast
来表达“饱和演员”?
答案 0 :(得分:15)
你可能会这样做:
#include <limits>
template<typename Target, typename Source>
Target saturation_cast(Source src) {
try {
return boost::numeric_cast<Target>(src);
}
catch (const boost::negative_overflow &e) {
return std::numeric_limits<Target>::lowest();
/* Or, before C++11:
if (std::numeric_limits<Target>::is_integer)
return std::numeric_limits<Target>::min();
else
return -std::numeric_limits<Target>::max();
*/
}
catch (const boost::positive_overflow &e) {
return std::numeric_limits<Target>::max();
}
}
(对于支持它的类型,错误情况也可以返回-inf / + inf)。
通过这种方式,你让Boost的numeric_cast
确定该值是否超出范围,然后可以做出相应的反应。
答案 1 :(得分:2)
嗯...如果上述方法有效,一般的解决办法可能就是:
template<typename TypeFrom, typename TypeTo>
TypeTo saturated_cast(TypeFrom value) {
TypeTo valueMin = std::numeric_limits<TypeTo>::min();
TypeTo valueMax = std::numeric_limits<TypeTo>::max();
return boost::numeric_cast<TypeTo>( std::max(std::min(value,valueMax),valueMin) );
}
希望我做对了......无论如何,你有这个概念:)
....顺便说一句:我认为你可以在这里使用static_cast
,因为在执行限制后你不能再超出范围了,所以你不需要额外检查numeric_cast
。< / p>
答案 2 :(得分:1)
如果您对C ++ 17没问题,但又不想在内部使用强制转换算法抛出异常,则可以使用std::clamp
进行一些包装来处理越界值。
template <typename TTo, typename TFrom>
constexpr TTo clamp_cast(const TFrom& src) noexcept
{
using to_limits = std::numeric_limits<TTo>;
using larger_type = std::conditional_t<(sizeof(TFrom) < sizeof(TTo)), TTo, TFrom>;
if constexpr (std::is_same_v<TTo, TFrom>)
{
// don't bother if it is the same type
return src;
}
else if constexpr (std::is_unsigned_v<TFrom>)
{
// if source is unsigned, we only need to worry about upper bound
return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
}
else if constexpr (std::is_unsigned_v<TTo>)
{
// source is signed, but destination is not
if (src < TFrom(0))
return TTo(0);
else
return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
}
else
{
// both are signed -- use regular clamping rules
return TTo(std::clamp(larger_type(src),
larger_type(to_limits::min()),
larger_type(to_limits::max())
)
);
}
}
使用基本上就是您所期望的:
static_assert(uint16_t(213) == clamp_cast<uint16_t>(213));
static_assert(uint16_t(65535) == clamp_cast<uint16_t>(9872431));
static_assert(uint16_t(0) == clamp_cast<uint16_t>(-98721));