10

C++ 核心指南有一个narrow转换,如果转换更改了值,则抛出该转换。查看该库的microsoft 实现

// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template <class T, class U>
T narrow(U u) noexcept(false)
{
    T t = narrow_cast<T>(u);
    if (static_cast<U>(t) != u)
        gsl::details::throw_exception(narrowing_error());
    if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))  // <-- ???
        gsl::details::throw_exception(narrowing_error());
    return t;
}

if第二个没看懂 它检查什么特殊情况,为什么还static_cast<U>(t) != u不够?


为了完整性:

narrow_cast只是一个static_cast

// narrow_cast(): a searchable way to do narrowing casts of values
template <class T, class U>
constexpr T narrow_cast(U&& u) noexcept
{
    return static_cast<T>(std::forward<U>(u));
}

details::is_same_signdess它的广告是这样的:

template <class T, class U>
struct is_same_signedness
    : public std::integral_constant<bool,
        std::is_signed<T>::value == std::is_signed<U>::value>
{
};
4

2 回答 2

14

这是检查溢出。让我们看看

auto foo = narrow<int>(std::numeric_limits<unsigned int>::max())

T将是int并且U将是unsigned int。所以

T t = narrow_cast<T>(u);

将给予商店-1t当你把它放回去时

if (static_cast<U>(t) != u)

-1转换回,std::numeric_limits<unsigned int>::max()因此检查将通过。尽管std::numeric_limits<unsigned int>::max()溢出int并且是未定义的行为,但这不是有效的强制转换。那么我们继续

if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))

由于符号不同,我们评估

(t < T{}) != (u < U{})

这是

(-1 < 0) != (really_big_number < 0)
==  true != false
==  true

所以我们抛出一个异常。如果我们走得更远并回绕 using 以使其t变为正数,那么第二次检查将通过,但第一次检查将失败,因为t它将是正数,并且转换回源类型仍然是相同的正值,而不是等于它的原始值。

于 2018-10-17T21:28:29.583 回答
1
if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))  // <-- ???

上面的检查是为了确保不同的签名不会让我们误入歧途。

第一部分检查它是否可能是一个问题,并包含在内以进行优化,所以让我们进入正题。

例如,取UINT_MAX(最大unsigned int的),并将其转换为signed.

假设INT_MAX == UINT_MAX / 2(这可能,虽然标准不能完全保证),结果将是(signed)-1,或者只是-1,一个负数。

虽然将其强制转换回原始值,但它通过了第一次检查,它本身不是相同的值,并且此检查会捕获错误。

于 2018-10-17T21:40:09.223 回答