Понимание gsl :: узкой реализации - PullRequest
0 голосов
/ 18 октября 2018

Основные принципы 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>
{
};

Ответы [ 2 ]

0 голосов
/ 18 октября 2018
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, отрицательное число.

Во время приведения.обратное возвращение приведет к исходному значению, таким образом, оно проходит первую проверку, само по себе оно не является тем же значением, и эта проверка ловит ошибку.

0 голосов
/ 18 октября 2018

Это проверка на переполнение.Давайте посмотрим на

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

T будет int и U будет unsigned int.Так что

T t = narrow_cast<T>(u);

даст магазин -1 в t.Когда вы преобразуете это обратно в

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

Итак, мы бросаемисключение.Если мы пойдем еще дальше и обернемся, используя, чтобы t стало положительным числом, тогда вторая проверка пройдет, но первая провалится, так как t будет положительной, и приведение к типу источника останется прежним.положительное значение, которое не равно его первоначальному значению.

...