理解 gsl::narrow 实现
C++ 核心指南有一个narrow
cast 如果转换改变了值就会抛出.查看库的微软实现:
The C++ Core Guidelines has a narrow
cast that throws if the cast changes the value. Looking at the microsoft implementation of the library:
// 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(t) != u
不够?
I don't understand the second if
. What special case does it check for and why isn't static_cast<U>(t) != u
enough?
为了完整性:
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>
{
};
推荐答案
这是检查溢出.来看看
auto foo = narrow<int>(std::numeric_limits<unsigned int>::max())
T
将是 int
而 U
将是 unsigned int
.所以
T
will be int
and U
will be unsigned int
. So
T t = narrow_cast<T>(u);
将在 t
中给 store -1
.当你把它放回去
will give store -1
in t
. When you cast that back in
if (static_cast<U>(t) != u)
-1
将转换回 std::numeric_limits
以便检查通过.这不是有效的转换,因为 std::numeric_limits
溢出 int
并且是未定义的行为.那么我们继续
the -1
will convert back to std::numeric_limits<unsigned int>::max()
so the check will pass. This isn't a valid cast though as std::numeric_limits<unsigned int>::max()
overflows an int
and is undefined behavior. So then we move on to
if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))
由于符号不同,我们评估
and since the signs aren't the same we evaluate
(t < T{}) != (u < U{})
这是
(-1 < 0) != (really_big_number < 0)
== true != false
== true
所以我们抛出一个异常.如果我们走得更远并返回使用使 t
成为正数,那么第二次检查将通过但第一个将失败,因为 t
将是正数,并且转换回源类型仍然是不等于其原始值的相同正值.
So we throw an exception. If we go even farther and wrap back around using so that t
becomes a positive number then the second check will pass but the first one will fail since t
would be positive and that cast back to the source type is still the same positive value which isn't equal to its original value.
相关文章