Я это понимаю 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;
}
Когда вы делаете
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();
}
который, ИМХО, намного легче читать и понимать.
Кастинг «один из них» int
оставляет вас с арифметической операцией, которая смешивает string::size_type
а также int
, В этом миксе тип без знака имеет тот же ранг, что и int
или выше, что означает, что тип без знака все еще «выигрывает»: ваш int
неявно преобразуется обратно в string::size_type
и расчеты выполняются в области string::size_type
, Ваше обращение в int
эффективно игнорируется.
Между тем, приведение результата к int
означает, что вы пытаетесь преобразовать значение, которое не вписывается в int
ассортимент. Поведение в таких случаях определяется реализацией. В реальных реализациях 2-дополнения нет ничего необычного в том, чтобы увидеть простое усечение представления, которое дает «правильный» результат. Это не очень хороший подход.
Если вы хотите выполнить это вычитание как подписанное, вы должны преобразовать и то и другое операнды для подписанных типов, гарантирующие, что целевой подписанный тип может представлять оба значения.
(Теоретически, вы можете обойтись без преобразования только одного операнда в тип со знаком, но для этого вам нужно будет выбрать тип, который может представлять весь диапазон string::size_type
.)