Почему string :: compare возвращает int?

102

Почему string::compareвозвращает intвместо меньшего типа, например shortили char? Насколько я понимаю, этот метод возвращает только -1, 0 или 1.

Вторая часть, если бы я должен был разработать метод сравнения, который сравнивает два объекта типа, Fooи я хотел бы вернуть только -1, 0 или 1, использовал бы shortили charвообще был бы хорошей идеей?

РЕДАКТИРОВАТЬ: меня исправили, string::compareне возвращает -1, 0 или 1, фактически возвращает значение> 0, <0 или 0. Спасибо, что держали меня в очереди, ребята.

Похоже, ответ примерно такой: нет причин возвращать тип меньшего, чем, intпотому что возвращаемые значения - это «rvalues», а эти «rvalue» не выигрывают от того, что они меньше типа int (4 байта). Кроме того, многие люди указали, что регистры большинства систем, вероятно, в intлюбом случае будут иметь размер , поскольку эти регистры будут заполнены независимо от того, даете ли вы им значение 1, 2 или 4 байта, нет реального преимущества в возврате меньшее значение.

РЕДАКТИРОВАТЬ 2: На самом деле похоже, что при использовании меньших типов данных, таких как выравнивание, маскирование и т. Д., Могут возникнуть дополнительные накладные расходы на обработку. По общему мнению, меньшие типы данных существуют для экономии памяти при работе с большим количеством данных, как в случай массива.

Узнал кое-что сегодня, еще раз спасибо, ребята!

Коди Смит
источник
Я думаю, что было бы лучше, если бы для этого можно было использовать более конкретный тип. Тот, который содержит только -1, 0 и 1 в стиле Ada95.
Sachin Kainth
23
В документации, на которую string::compare()вы ссылаетесь, четко указано, что возвращаемое значение <0, 0 и> 0, а не -1, 0 и 1.
Captain Obvlious
6
В чем будет преимущество использования shortили charвместо int? Большинство архитектур собираются хранить возвращаемое значение функции в регистре, и в регистр intбудет помещаться an так же хорошо, как shortили char. А использование charчисловых типов - всегда плохая идея, особенно когда вам нужно гарантировать правильную обработку подписанных значений.
Коди Грей
7
Капитан Обвлиус, ваше имя и комментарий ... Просто бесценно.
Cody Smith
2
Использование charбыло бы плохой идеей, поскольку проверка кода для возвращаемого значения, если оно меньше нуля, не удастся на платформах, где нет charзнака.
milleniumbug

Ответы:

113

Во-первых, спецификация заключается в том, что он вернет значение меньше, равно или больше 0, не обязательно -1или 1. Во-вторых, возвращаемые значения - это r-значения, подлежащие полному продвижению, поэтому возвращать что-то меньшее нет смысла.

В C ++ (как и в C) каждое выражение является либо rvalue, либо lvalue. Исторически эти термины относятся к тому факту, что lvalue появляются слева от присваивания, тогда как as rvalues ​​могут появляться только справа. Сегодня простое приближение для неклассовых типов состоит в том, что lvalue имеет адрес в памяти, а rvalue - нет. Таким образом, вы не можете взять адрес rvalue, а cv-квалификаторы (условие «доступ») не применяются. В терминах C ++ rvalue, не имеющий типа класса, является чистым значением, а не объектом. Возвращаемое значение функции - rvalue, если оно не имеет ссылочного типа. (Неклассовые типы, которые помещаются в регистр, почти всегда будут возвращаться, например, в регистре, а не в памяти.)

Для типов классов проблемы немного сложнее из-за того, что вы можете вызывать функции-члены по rvalue. Это означает, что rvalues ​​фактически должны иметь адреса для this указателя и могут быть квалифицированы cv, поскольку cv-квалификация играет роль в разрешении перегрузки. Наконец, C ++ 11 вводит несколько новых различий для поддержки ссылок rvalue; они также в основном применимы к типам классов.

Под интегральным продвижением понимается тот факт, что когда целочисленные типы, меньшие чем an int, используются в качестве r-значений в выражении, в большинстве контекстов они будут повышены до int. Таким образом, даже если short a, b;в выражении объявлена ​​переменная a + b, оба aи bповышаются intдо того, как произойдет добавление. Точно так же, если я пишу a < 0, сравнение выполняется по значению a, преобразованному в int. На практике очень мало случаев, когда это имеет значение, по крайней мере, на машинах с дополнением до 2, где целочисленная арифметика оборачивается (т.е. все, кроме очень немногих экзотических вещей, сегодня - я думаю, что мэйнфреймы Unisys являются единственными оставшимися исключениями). Тем не менее, даже на более распространенных машинах:

short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;

должен давать разные результаты: первый эквивалент sizeof( short ), второй sizeof( int )(из-за целостного продвижения).

Эти две проблемы формально ортогональны; rvalues ​​и lvalues ​​не имеют ничего общего с интегральным продвижением. За исключением ... интегральное продвижение применяется только к rvalue, и в большинстве (но не во всех) случаях, когда вы использовали бы rvalue, результатом было бы полное продвижение. По этой причине действительно нет причин возвращать числовое значение в чем-то меньшем, чем int. Есть даже очень веская причина не возвращать его как символьный тип. Например <<, перегруженные операторы часто ведут себя по-разному для типов символов, поэтому вы хотите возвращать символы только как типы символов. (Вы можете сравнить разницу:

char f() { return 'a'; }
std::cout << f() << std::endl;      //  displays "a"
std::cout << f() + 0 << std::endl;  //  displays "97" on my machine

Разница в том, что во втором случае добавление вызвало интегральное продвижение, что приводит <<к выбору другой перегрузки .

Джеймс Канце
источник
46
Было бы неплохо, если бы вы могли объяснить больше return values are rvalues, subject to integral promotionв своем ответе.
Элвин Вонг
«возвращаемые значения - это rvalue ... поэтому нет смысла возвращать что-то меньшее» НРАВИТСЯ
masoud
1
@AlvinWong: См. Ответы на вопрос, почему символьные литералы C используют целые числа вместо символов? для получения дополнительной справочной информации.
Джесси Гуд
Хотел бы я снова поставить +1 после того, как вы добавили превосходное объяснение.
Коди Грей
Что, если это было signed char? Будет ли он вести себя так же, как подписанный char, или это будет другой тип?
user541686
41

Он намеренно не возвращает -1, 0 или 1.

Он позволяет (обратите внимание, что это не для строк, но в равной степени относится к строкам)

int compare(int *a, int *b)
{
   return *a - *b;
}

что намного менее громоздко, чем:

int compare(int *a, int *b)
{
   if (*a == *b) return 0;
   if (*a > *b) return 1;
   return -1;
}

что вы должны будете сделать [или что-то в этом роде], если вам нужно вернуть -1, 0 или 1.

И это работает и для более сложных типов:

class Date
{
    int year;
    int month;
    int day;
}

int compare(const Date &a, const Date &b)
{
   if (a.year != b.year) return a.year - b.year;
   if (a.month != b.month) return a.month - b.month;
   return a.day - b.day;
}

В случае строки мы можем сделать это:

int compare(const std::string& a, const std::string& b)
{
   int len = min(a.length(), b.length());

   for(int i = 0; i < len; i++)
   {
      if (a[i] != b[i]) return a[i] - b[i];
   }
   // We only get here if the string is equal all the way to one of them
   // ends. If the length isn't equal, "longest" wins. 
   return a.length() - b.length();
}
Матс Петерссон
источник
8
У вашей первой compareфункции есть проблемы с переполнением, которые (к счастью) не применяются одинаково, если она занимает char*и charменьше чем int. Например, if *ais MAX_INTand *bis -1then *a - *bis UB, но если реализация решит определить свое поведение, результат почти наверняка будет отрицательным.
Стив Джессоп,
1
Проблема с вашим последним примером: length()возвращает a size_t, которое может быть больше, чем int
F'x
Да, это может быть проблемой, если длина ваших строк превышает 2 ГБ. Я сделал длинные строки размером 1 ГБ в качестве тестового примера для хранения вещей в FIFO один раз. Но конечно, кто-то, имеющий дело со строкой, содержащей MPEG, закодированный как Base64 или что-то подобное, вполне может столкнуться с этой проблемой ...
Матс Петерссон
@MatsPetersson, это скорее фундаментальная проблема, потому что вопрос в том, «почему он возвращает int?»
F'x
Что ж, я уверен, что это истерика - я имею в виду исторические причины - и, вероятно, потому, что она совместима с strcmp / memcmp и другими операциями сравнения типов.
Mats Petersson
25

int обычно (имеется в виду на большинстве современных аппаратных средств) целое число того же размера, что и системная шина и / или регистры процессора, что называется машинным словом. Поэтому int обычно передается быстрее, чем типы меньшего размера, потому что он не требует выравнивания, маскирования и других операций.

Меньшие типы существуют в основном для оптимизации использования ОЗУ для массивов и структур. В большинстве случаев они жертвуют несколькими циклами ЦП (в форме операций выравнивания) для лучшего использования ОЗУ.

Если вам не нужно, чтобы возвращаемое значение было числом со знаком или без знака с размером centain (char, short…), вам лучше использовать int, поэтому стандартная библиотека делает это.

Tobia
источник
Отличный способ объяснить аппаратную сторону вещей понятным образом.
Ogre Psalm33
10

Это C-ism.

Когда C требовал compareфункций -типа, они всегда возвращали int. C ++ просто продвинул это вперед (к сожалению).

Однако, на практике, это, intвероятно, самый быстрый способ, так как обычно это размер регистров используемой системы. (Умышленно расплывчато.)

Алекс Чемберлен
источник
1
На самом деле shortи charможет налагать штрафы на производительность, например, 255+7имеет другое значение для a charи intпоэтому правильная реализация не может обязательно просто хранить a, charгде intможет идти, не заботясь о передаче его семантики. Компиляторы не обязательно оптимизируют создаваемую этим неэффективность.
Джек Эйдли
10

На самом деле метод не возвращает целое число в наборе { -1, 0, 1 }; фактически это может быть любое целое значение.

Зачем? Основная причина, по которой я могу думать, заключается в том, что intэто должно быть значение «естественного размера» для архитектуры; операции со значениями такого размера обычно выполняются как минимум так же быстро (а во многих случаях и быстрее), чем операции с меньшими или большими значениями. Таким образом, это случай, когда реализации достаточно провисания для использования того, что является самым быстрым.

Джон
источник
4

если бы мне нужно было разработать метод сравнения, который сравнивает два объекта типа Foo, и я хотел бы вернуть только -1, 0 или 1, было бы использование short или char вообще было хорошей идеей?

Было бы неплохо. Лучшим способом было бы вернуть bool (если вы хотите сравнить только при равенстве) или enum (для получения дополнительной информации):

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}
БЈовић
источник
3
«Было бы неплохо». У вас есть для этого объяснение?
jrok
4

Предположим, некоторые люди меняют код с C на C ++. Решили заменить strcmpна string::compare.

Так как strcmpвозвращается int, string::compareвернуть легче int, в подарок.

Масуд
источник
2

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

Кроме того, возвращаемое значение не просто -1, 0или 1но <0, 0или >0.

Кроме того, как уже упоминалось, поскольку возврат подлежит комплексному продвижению , нет смысла уменьшать его.

Шафик Ягмур
источник
-1

потому что логическое возвращаемое значение может быть только двумя возможными значениями (истина, ложь), а функция сравнения может возвращать три возможных значения (меньше, равно, больше).

Обновить

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

MDMoore313
источник
7
Нигде в вопросе ничего не говорится о возврате логического типа. Собственно, он специально предлагает shortи в charкачестве альтернативы int.
Коди Грей