почему вычитание переполняется при использовании static_cast?

Я это понимаю s1.size() - s2.size() когда падает s2 больше, потому что это вычитание unsigned,
Зачем кастовать их int не приводит к целочисленному вычитанию?
Почему все это дает мне правильный результат? Я ожидал, что он оценит то, что находится внутри круглых скобок, затем недопустимое значение, которое даст большое число, а затем приведение к int не будет иметь значения. Что мне не хватает?

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

bool isShorter(const string &s1, const string &s2) {
return (static_cast<int>(s1.size()) - s2.size() < 0) ? true : false; // underflows
//return (static_cast<int>(s1.size() - s2.size()) < 0) ? true : false; // this works

}
int main() {
string s, t;
getline(cin, s);
getline(cin, t);
cout << "s: " << s << endl;
cout << "t: " << t << endl;
cout << "printing shorter string of the two..." << endl;
cout << ((isShorter(s, t)) ? s : t) << endl;
}

2

Решение

Когда вы делаете

static_cast<int>(s1.size()) - s2.size()

Вы конвертируете s1.size() к int а потом, когда вы вычитаете s2.size() от того что int повышен до того же типа, что и s2.size() а затем вычитается. Это означает, что у вас все еще есть целое вычитание без знака, и, поскольку оно не может быть отрицательным, оно будет округлено до большего числа. Это ничем не отличается от выполнения s1.size() - s2.size(),

У вас то же самое с

static_cast<int>(s1.size() - s2.size())

С дополнительным бонусом возможного переполнения со знаком, что является неопределенным поведением. Вы все еще делаете вычитание целого числа без знака, так что если s1 меньше чем s2 чем вы обернетесь к большому количеству.

Что вам нужно сделать, это конвертировать оба s1.size() а также s2.size() целому типу со знаком, чтобы получить вычлененное целочисленное вычитание. Это может выглядеть как

static_cast<ptrdiff_t>(s1.size())  - static_cast<ptrdiff_t>(s2.size())

И теперь вы на самом деле получите отрицательное число, если s1.size() меньше чем s2.size(),


Следует отметить, что всего этого можно избежать, используя оператор «меньше». Ваша функция может быть переписана, чтобы быть

bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}

который, ИМХО, намного легче читать и понимать.

3

Другие решения

Кастинг «один из них» int оставляет вас с арифметической операцией, которая смешивает string::size_type а также int, В этом миксе тип без знака имеет тот же ранг, что и int или выше, что означает, что тип без знака все еще «выигрывает»: ваш int неявно преобразуется обратно в string::size_type и расчеты выполняются в области string::size_type, Ваше обращение в int эффективно игнорируется.

Между тем, приведение результата к int означает, что вы пытаетесь преобразовать значение, которое не вписывается в intассортимент. Поведение в таких случаях определяется реализацией. В реальных реализациях 2-дополнения нет ничего необычного в том, чтобы увидеть простое усечение представления, которое дает «правильный» результат. Это не очень хороший подход.

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

(Теоретически, вы можете обойтись без преобразования только одного операнда в тип со знаком, но для этого вам нужно будет выбрать тип, который может представлять весь диапазон string::size_type.)

2

По вопросам рекламы [email protected]