Чому рядок :: порівняння повертає Int?


102

Чому string::compareповертається intзамість меншого типу типу shortабо char? Я розумію, що цей метод повертає лише -1, 0 або 1.

Друга частина, якби я створив метод порівняння, який порівнював два об'єкти типу, Fooі я хотів повернути лише -1, 0 або 1, використовував би shortчи charвзагалі хорошу ідею?

EDIT: Мені виправлено, string::compareне повертає -1, 0 або 1, він фактично повертає значення> 0, <0 або 0. Дякую за те, що тримаєте мене в черзі, хлопці.

Здається, що відповідь приблизно, немає причин повертати тип менше, ніж intтому, що значення повернення є "rvalues", а ті "rvalues" не мають користі від того, щоб бути меншими за тип int (4 байти). Крім того, багато людей вказували, що регістри більшості систем, ймовірно, будуть такими ж розмірами int, оскільки ці регістри заповнюватимуться, надаючи їм значення 1, 2 або 4 байтів, немає реальної переваги поверненню менше значення.

EDIT 2: Насправді, схоже, може бути додаткова обробка накладних даних при використанні менших типів даних, таких як вирівнювання, маскування тощо. Загальний консенсус полягає в тому, що існують менші типи даних для збереження пам’яті при роботі з великою кількістю даних, як у випадок масиву.

Навчився чогось сьогодні, ще раз дякую хлопці!


Я думаю, що було б краще, якби був більш конкретний тип, який можна було б використати для цього. Такий, який містить лише -1, 0 та 1 у стилі Ada95.
Сачин Карінт

23
У документації, на string::compare()яку ви посилаєтесь, чітко зазначено, що повернене значення <0, 0 і> 0 -не- -1, 0 і 1.
Капітан Обвілій

6
Яка була б перевага використання shortабо charзамість цього int? Більшість архітектур збираються зберігати значення, що повертається функції в регістрі, і intпоміщаються в регістрі точно так же , як shortі char. І використовувати charдля числових типів завжди погана ідея, особливо коли потрібно гарантувати, що підписані значення обробляються правильно.
Коді Грей

7
Капітан Обвіліус, ваше ім'я та коментар ... Просто безцінно.
Коді Сміт

2
Використання charбуло б поганою ідеєю, оскільки перевірка коду на повернене значення, якщо воно менше нуля, не вдасться на платформах, де charне підписано.
тисячоліття

Відповіді:


113

По-перше, специфікація полягає в тому, що вона поверне значення, менше, рівне або більше 0, не обов'язково -1або 1. По-друге, значення повернення - це значення, що підлягають цілісній рекламі, тому немає сенсу повертати щось менше.

У C ++ (як і в C) кожен вираз є або rvalue, або lvalue. Історично терміни посилаються на той факт, що lvalues ​​з’являються зліва від завдання, де rvalues ​​можуть відображатися лише праворуч. Сьогодні просте наближення для некласових типів полягає в тому, що lvalue має адресу в пам'яті, а rvalue - ні. Таким чином, ви не можете прийняти адресу рецензії, а cv-кваліфікатори (яка умова "доступ") не застосовуються. У C ++ термінах, значення, яке не має типу класу, є чистим значенням, а не об'єктом. Повернене значення функції - це значення, якщо воно не має посилального типу. (Некласові типи, які вміщуються в регістрі, майже завжди будуть повертатися в регістр, наприклад, а не в пам'ять.)

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

Інтегральне просування означає те, що коли цілісні типи, менші за а int, використовуються як rvalues ​​у виразі, у більшості контекстів вони будуть просуватися до 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 ​​не мають нічого спільного з цілісним просуванням. За винятком ... цілісне просування стосується лише значень rvalues, і більшість (але не всі) випадків, коли ви використовуєте 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у своїй відповіді.
Альвін Вонг

«Зворотні значення rvalues ... так що немає ніякого сенсу в поверненні нічого менше» LIKE IT
Масуд

1
@AlvinWong: Подивіться відповіді на те, Чому букви літералів символів C замість знаків? для отримання додаткової довідкової інформації.
Джессі Гуд

Я б хотів, щоб я міг поставити +1 ще раз після чудового пояснення, яке додала ваша редакція.
Коді Грей

Що робити, якщо це було signed char? Чи поводитиметься він так само, як підписаний char, чи був би інший тип?
користувач541686

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. Наприклад, якщо *aє MAX_INTі *bє, -1то *a - *bє UB, але якщо реалізація вирішує визначити свою поведінку, результат майже напевно негативний.
Стів Джессоп

1
Проблема з вашим останнім прикладом: length()повертає a size_t, який може бути більшим, ніж int
F'x

Так, це може бути проблемою, якщо у ваших струн більше 2 Гб. Я зробив 1 Гб довгими рядками як тестовий корпус для зберігання речей у фіфо. Але впевнено, хтось, що має справу з рядком, що містить MPEG, кодований як Base64, або якийсь подібний, цілком може натрапити на цю проблему ...
Mats Petersson

@MatsPetersson - це скоріше фундаментальна проблема, оскільки питання полягає в тому, "чому він повертає інт?"
F'x

Ну, я впевнений, що це істерично - я маю на увазі історичні причини - і, мабуть, так, що воно сумісне зі strcmp / memcmp та іншими операціями порівняння типу.
Матс Петерсон

25

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

Менші типи існують, головним чином, для оптимізації використання оперативної пам’яті для масивів і структур. У більшості випадків вони торгують декількома циклами процесора (у вигляді операцій з відчуженням) для кращого використання оперативної пам’яті.

Якщо вам не потрібно примусово повертати значення, щоб бути підписаним або непідписаним номером розміру десятиліття (char, короткий ...), вам краще використовувати int, саме тому стандартна бібліотека робить це.


Чудовий спосіб пояснити апаратну сторону речей таким чином, що має сенс.
Псалом Огре3333

10

Це С-ізм.

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

Однак повернення intреальності є, мабуть, найшвидшим способом, оскільки це, як правило, розмір регістрів використовуваної системи. (Навмисно розпливчасте.)


1
Насправді shortі charможе накладати штрафні санкції за ефективність, наприклад, 255+7має інше значення для char, intтому правильна реалізація не може просто зберігати те, charкуди intможе піти, не піклуючись про передачу її семантики. Компілятори не обов'язково оптимізують неефективність, яку це нав'язує.
Джек Едлі

10

Метод насправді не повертає ціле число у наборі { -1, 0, 1 }; насправді це може бути будь-яка цілісна цінність.

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


4

якби я створив метод порівняння, який порівнював би два об'єкти типу Foo, і я хотів повернути лише -1, 0 або 1, було б використання коротких або знаків взагалі гарною ідеєю?

Було б нормально ідея. Кращим способом було б повернути бул (якщо хочете лише порівняти, якщо рівний), або перерахувати (для отримання додаткової інформації):

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

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

Оновлення

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


7
Ніде в питанні нічого не сказано про повернення булевого типу. Насправді він спеціально пропонує shortі charяк альтернативу int.
Коді Грей
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.