Чому використання кортежів у С ++ не зустрічається частіше?


124

Чому, схоже, ніхто не використовує кортежі в C ++, ні бібліотеку Boost Tuple, ні стандартну бібліотеку для TR1? Я прочитав багато C ++-коду, і дуже рідко я бачу використання кортежів, але я часто бачу багато місць, де кортежі вирішують багато проблем (як правило, повертають декілька значень з функцій).

Кортежі дозволяють робити всілякі класні речі, як це:

tie(a,b) = make_tuple(b,a); //swap a and b

Це, звичайно, краще, ніж це:

temp=a;
a=b;
b=temp;

Звичайно, ви завжди могли це зробити:

swap(a,b);

Але що робити, якщо ви хочете повернути три значення? Зробити це можна за допомогою кортежів:

tie(a,b,c) = make_tuple(b,c,a);

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

Чи є якісь недоліки кортежів, про які я не думаю? Якщо ні, то чому їх рідко використовують? Вони повільніші? Або просто люди не звикли до них? Чи корисно використовувати кортежі?


17
+1 для хитрого фокусу з заміною кортежу :)
kizzx2

10
a = a ^ b; b = a ^ b; a = a ^ b;
Джерардо Марсет

3
Кортежі IMO зручні у слабких мовах набору тексту чи мовах, якими вони є рідні структури. Наприклад, в Python або PHP вони просто полегшують життя, тоді як в C ++ надто багато вводити текст (щоб сконструювати його з шаблону) і занадто мало переваг.
doc

4
Коментар до ОП: Я думаю, що поточна прийнята відповідь вже застаріла до того, що фактично помиляється. Можливо, ви захочете переглянути вибір прийнятої відповіді.
улідко

8
@GerardoMarset Ви серйозно?
thesaint

Відповіді:


43

Тому що це ще не стандарт. Все, що є нестандартним, має набагато більшу перешкоду. Pieces of Boost стали популярними, тому що програмісти вимагали їх. (hash_map стрибає на розум). Але хоча кортеж зручний, люди не так перемагають і чітко виграють.


1
Люди, здається, використовують інші частини Boost як божевільні. Хоча звичайно хеш-карти взагалі набагато корисніші, ніж кортежі.
Зіфре

Я не знаю специфіки того, що ти бачиш, але я здогадуюсь, що частини, які люди використовують як божевільні, - це особливості, які вони справді дуже хотіли. Таким чином (знову вгадавши) популярність хеш-карти, підрахований покажчик тощо. Кортеж зручний, але це не те, що вискакує, щоб заповнити яму. Виплата не очевидна. Як часто потрібно обертати саме N об’єктів? (На відміну від необхідності обертати довільно довгі вектори). І люди звикли або передавати повернені значення за посиланням, або повертати невеликі класи або структури.
Алан Де Смет

20
Наразі це частина C ++ 11 стандарту зараз: en.cppreference.com/w/cpp/utility/tuple
Роман Сусі

124

Цинічна відповідь полягає в тому, що багато людей програмують на C ++, але не розуміють та / або використовують функціонування вищого рівня. Іноді це відбувається тому, що їх не дозволяють, але багато хто просто не намагаються (або навіть розуміють).

Як приклад, що не підсилює: скільки людей використовують функціонал, знайдений у <algorithm> ?

Іншими словами, багато програмістів на C ++ є просто програмістами C, що використовують компілятори C ++, а можливо, std::vectorі std::list. Це одна з причин, чому використання boost::tupleне є більш поширеним.


18
-1 від мене, оскільки програмісти на C ++ не такі німі, як ця відповідь робить їх звуковими.
користувач541686

5
@Mehrdad Переглянувши багато C ++ коду, як комерційного, так і ні, читаючи тонни матеріалів C ++, я думаю, що досить впевнено сказати, що дуже велика частина розробників "C ++" - це лише розробники C, які не змогли отримати чистий C компілятор. Наприклад, шаблонів майже повністю не вистачає у більшості матеріалів (те, чого я багато навчився любити). Дивні макро хаки поширені, а простір імен сильно не використовується.
Чіткіший

5
Дурниця відповідь. Якщо хтось їм потрібен, він їх наздожене. Вони не потрібні; тому вони не використовуються. Сказати, що їх не використовують, тому що їх легко зрозуміти - це погано.
Майкл Чурдакіс

9
Коментар @Michael Дурниці. Нічого не потрібно в програмуванні, як тільки ви отримаєте повний підмножина мови Тюрінга. Відсутність використання, безумовно, не означає, що всі розуміють конструкції C ++ вищого рівня та вирішили не використовувати їх.
Трей Джексон

4
Tbh Я ніколи не мав потреби у std :: tuple поза метапрограмування варіативного шаблону. Не було жодного сенсу в житті, коли я сів із сумним обличчям, думаючи: «Якби у мене були ці кортежі». Насправді, дивлячись на кортежі, я думаю, "для чого б хто-небудь з них потребував (я б не вважав себе здоровим)". Люди, що знаходяться поза метапрограмуванням, здається, використовують їх як "Анонімну структуру", яка є такою некрасивою і вказує на сильну відсутність якості коду та ремонтопридатності в головах.
thesaint

23

Синтаксис кортежу C ++ може бути трохи більш багатослівним, ніж хотіли б більшість людей.

Поміркуйте:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

Тож, якщо ви хочете широко використовувати кортежі, ви або отримуєте кортежі typedefs скрізь, або всюди отримуєте дратівливі назви довгих типів. Мені подобаються кортежі. Я використовую їх при необхідності. Але, як правило, це обмежується кількома ситуаціями, наприклад, N-елементним індексом або при використанні мультимап для прив'язки пар ітераторів діапазону. І це, як правило, в дуже обмеженому масштабі.

Це все дуже некрасиво і хитро виглядає в порівнянні з чимось на кшталт Haskell або Python. Коли C ++ 0x потрапить сюди, і ми отримаємо "авто" ключові слова, кортежі почнуть виглядати набагато привабливіше.

Корисність кортежів обернено пропорційна кількості натискань клавіш, необхідних для їх оголошення, упаковки та розпакування.


Більшість людей будуть робити "збільшення простору імен;" і не потрібно вводити boost ::. Я не думаю, що введення кортежу - це велика проблема. Це сказав, я думаю, у вас є пункт. Авто може змусити значно більше людей почати використовувати кортежі.
Зіфре

2
@Zifre: проблема полягає в тому, що ви не повинні робити "використання простору імен X" у файлі заголовка, оскільки це змушує забруднювати простір імен і підриває простори імен.
Містер Фооз

1
Ах, так, я забуваю про заголовки. Але всередині програмного коду вам не потрібно турбуватися про це. І як тільки у нас є C ++ 0x, ми можемо використовувати авто, яке повинно усунути багато введення тексту.
Зіфре

19
Це тільки я? Я не думаю, що він мав на увазі збереження введення 7 символів "boost ::", а інші 33 символи . Це чорт з великою кількістю набору класів, особливо якщо вони теж є простором імен. Візьміть boost :: tuple <std :: string, std :: set <std :: string>, std :: vector <Мій :: Scoped :: LongishTypeName>> як смішний приклад.
Огр Псалом3333

10

Для мене це звичка, руки вниз: кортежі не вирішують жодних нових проблем для мене, лише декілька я вже впораюся. Поміняти значення все ще відчувається легше старомодним способом - і, що ще важливіше, я дійсно не замислююся над тим, як поміняти "краще". Це досить добре, як є.

Особисто я не думаю, що кортежі є чудовим рішенням для повернення кількох значень - це звучить як робота для structs.


4
"Я не дуже думаю про те, як замінити" краще "." - Коли я пишу код, я пишу помилки. Зменшення складності коду зменшує кількість помилок, які я записую. Я ненавиджу створювати одні і ті ж помилки знову і знову. Так, я дійсно замислююся над тим, як <strike> поміняти </code> краще . Менше рухомих частин (LOC, тимчасові змінні, ідентифікатори для введення тексту), більш читабельний код; Хороший кодекс.
sehe

Погодьтеся. Клас, загорнутий в автоматичний вказівник або розумний вказівник, зберігає тип. Я використовував кортежі один раз, але потім переписав код за допомогою класів. retValue.state більш зрозумілий, ніж retValue.get <0> ().
Валентин Хайніц

1
@sehe: Моя мета також краще писати, читабельніший код. Додавання більшої кількості синтаксису має вартість, і я не вважаю, що «краща заміна» виправдовує розумові витрати на роздуми про ще більше типів синтаксису для кожного прочитаного рядка коду.
ojrac

8

Але що робити, якщо ви хочете повернути три значення?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

ОК, тож із значеннями 4 і т.д., з часом n-кортеж стає меншим кодом, ніж n-1 свопів. І за допомогою заміну за замовчуванням це робить 6 завдань замість 4, які ви мали б, якби ви реалізували шаблон три циклу самостійно, хоча, сподіваюся, компілятор вирішить це для простих типів.

Ви можете придумати сценарії, коли свопи є нелегкими або недоречними, наприклад:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

трохи незручно розпакувати.

Справа, однак, є відомі способи вирішення найпоширеніших ситуацій, для яких кортежі корисні, і, отже, немає великої терміновості братися за кортежі. Якщо нічого іншого, я не впевнений, що:

tie(a,b,c) = make_tuple(b,c,a);

не робить 6 копій, що робить його абсолютно непридатним для деяких типів (колекції є найбільш очевидними). Не соромтеся переконувати мене, що кортежі - це гарна ідея для "великих" типів, кажучи, що це не так :-)

Для повернення декількох значень кортежі ідеальні, якщо значення несумісних типів, але деяким людям вони не подобаються, якщо можливо, що абонент може отримати їх у неправильному порядку. Деяким людям взагалі не подобається кілька повернених значень і не хочуть заохочувати їх використання, полегшуючи їх. Деякі люди просто віддають перевагу названим структурам для параметрів входу та виходу, і, ймовірно, не вдалося переконати бейсбольною битою використовувати кортежі. Без обліку смаку.


1
Ви точно не хочете міняти вектори кортежами. Я думаю, що заміна трьох елементів, безумовно, більш зрозуміла з кортежами, ніж з двома свопами. Що стосується декількох повернених значень, то параметри - це зло, структури - це додаткове набрання тексту, і напевно є випадки, коли потрібно кілька повернених значень.
Зіфре

знаю, для чого ти використовуєш кортежі (і я знаю, для чого я б їх використовував, якщо випала така нагода, хоча я не думаю, що це було). Я здогадуюсь, чому інші люди не користуються ними, навіть якщо вони їх знають. наприклад, тому що вони не згодні з "парами злі" ...
Стів Джессоп

Чи знаєте ви, чи можемо ми замінити "tie (a, b, c) = make_tuple (b, c, a);" за "краватка (a, b, c) = краватка (b, c, a);" ?
Rexxar

2
Краватка (ну, технічно, ярус) - це кортеж, зроблений з посиланнями, що не мають конкуренції. Я не можу знайти документацію підвищення, яка говорить про те, що гарантує оператор = та конструктор копій для зв’язування / кортежу, коли деякі з посилань, що займаються, мають один і той самий реферад. Але це те, що потрібно знати. Наївна реалізація оператора = очевидно, може піти не так ...
Стів Джессоп

1
@Steve: А оскільки зв'язки стосуються запобігання копіювання (вони повинні працювати для не копіюваних типів; зауважте, що LHS може бути зовсім іншим), все-таки все має піти не так (подумайте про об'єкти класу, які не є POD). Уявіть собі, як ви писали б ту саму логіку без використання temps.
сехе

7

Як зазначали багато людей, кортежі просто не такі корисні, як інші функції.

  1. Перемикачі та обертові трюки - це лише пристосування. Вони вкрай плутають тих, хто їх раніше не бачив, і оскільки це майже всі, ці трюки - це лише погана практика інженерного програмного забезпечення.

  2. Повернення декількох значень за допомогою кортежів набагато менше самодокументування, ніж альтернативи - повернення названих типів або використання названих посилань. Без цього самодокументування легко переплутати порядок повернених значень, якщо вони взаємно конвертуються і не будуть будь-якими мудрішими.


6

Не кожен може використовувати прискорення, і TR1 поки що не доступний у широкому масштабі.


3
Багато людей використовують Boost. Ці люди також могли використовувати кортежі.
Зіфре

3
Ви запитали, чому люди їх не використовують, і я дав одну відповідь.
Брайан Ніл

2
Для нижчого виборця: мені трапляється працювати там, де політично неможливо використовувати прискорення, і навіть на цю дату ланцюжок інструментів компілятора, який ми використовуємо (для вбудованої системи), не підтримує TR1 / C ++ 11.
Брайан Ніл

5

При використанні C ++ у вбудованих системах втягування бібліотек Boost стає складнішим. Вони з'єднуються один з одним, тому розмір бібліотеки зростає. Ви повертаєте структури даних або використовуєте параметр передачі замість кортежів. При поверненні кортежів у Python структура даних знаходиться в порядку та типі повернених значень, просто не явні.


5

Ви їх рідко бачите, тому що добре розроблений код їх зазвичай не потребує - в дикій природі не так багато випадків, коли використання анонімної структури перевершує використання іменного. Оскільки весь кортеж справді являє собою анонімну структуру, більшість кодерів у більшості ситуацій просто йдуть із реальною справою.

Скажімо, у нас є функція "f", де повернення кортежу може мати сенс. Як правило, такі функції зазвичай досить складні, що можуть вийти з ладу.

Якщо "f" CAN не вдається, вам потрібен стан повернення - зрештою, ви не хочете, щоб абоненти повинні перевіряти кожен параметр, щоб виявити збій. "f", ймовірно, вписується в шаблон:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

Це не дуже, але подивіться, наскільки потворна альтернатива. Зауважте, що мені все ще потрібно значення статусу, але код не є більш читабельним і не коротшим. Це, мабуть, і повільніше, оскільки я маю на себе вартість 1 примірника з кортежем.

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

Інший, істотний мінус тут прихований - з "ReturnInts" я можу додати повернення al "f", змінивши "ReturnInts" БЕЗ ЗМІНИ "F" INTERFACE. Рішення кортежу не пропонує тієї критичної характеристики, що робить його неповноцінною відповіддю на будь-який код бібліотеки.


1
Винятки роблять цей інтерфейс набагато чистішим.
Девід Стоун

Щоб бути чесним (і вкрай запізнитися на вечірку), ви можете полегшити читабельність, встановивши using std::tuple;та просто скориставшись tupleкодом.
Alrekr

2
Використання tupleробить код менш читабельним, не більше. Більшість кодів у наші дні містить дуже велику кількість символів - бачення std::tupleдає зрозуміти очей, що саме таке.
Tom Swirly

3

Безумовно, кортежі можуть бути корисними, але, як згадувалося, є трохи накладних витрат і перешкода або два, через які вам доведеться перестрибнути, перш ніж навіть реально їх використовувати.

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

Взагалі кажучи, не у всіх уже встановлено Boost, і я, звичайно, не переживаю труднощів з його завантаженням і налаштуванням каталогів, що включають, для роботи з ним лише для його функцій. Я думаю, ви побачите, що люди, які вже використовують Boost, швидше знайдуть кортежі у своїх програмах, ніж користувачі, що не підсилюють Boost, і мігранти з інших мов (Python приходить на думку) швидше просто засмучуються відсутністю кортежів в C ++, ніж вивчити способи додавання кортежної підтримки.


1

Оскільки сховище даних std::tupleмає найгірші характеристики як a, так structі масиву; весь доступ базується на n-й позиції, але не можна переходити через atuple допомогою afor циклу.

Отже, якщо елементи в tupleпонятійному понятті є масивом, я буду використовувати масив, і якщо елементи не є концептуально масивом, структура (яка назвала елементи) є більш ретельною. ( a.lastnameє більш пояснювальним, ніжstd::get<1>(a) ).

Це залишає перетворення, про яке йдеться в ОП, як єдиний життєздатний приклад для кортежів.


0

У мене є відчуття, що багато хто використовує Boost.Any і Boost.Variant (з деякою технікою) замість Boost.Tuple.


Чому ви торгуєте ефективним статичним введенням для чогось подібного?
Зіфре

Boost.Variant повністю безпечний для типу.
user21714

1
Ой, так, це безпечно для набору даних, але це робить текст під час виконання.
Зіфре

5
Я не бачу, як Tuple можна замінити Будь-який / Варіант. Вони не роблять те саме.
Манкарсе

1
@Zifre Я не можу говорити за автора, але я думаю, що тут розуміється використання їх разом з іншими типами контейнерів.
Тім Сегейн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.