Почему использование кортежей в С++ не является более распространенным?

Почему никто не использует кортежи в С++, Boost Tuple Library или стандартную библиотеку для TR1? Я прочитал много кода на С++ и очень редко вижу использование кортежей, но часто вижу множество мест, где кортежи будут решать многие проблемы (обычно возвращая несколько значений из функций).

Кортежи позволяют вам делать всевозможные классные вещи, такие как:

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);

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

Есть ли большие недостатки в кортежах, о которых я не думаю? Если нет, то почему они редко используются? Они медленнее? Или просто люди не привыкли к ним? Полезно ли использовать кортежи?

Ответ 1

Потому что он еще не стандартный. Все, что нестандартно, имеет гораздо более высокий барьер. Pieces of Boost стали популярными, потому что программисты требовали их. (hash_map бросается в голову). Но в то время как кортеж удобен, это не такая ошеломляющая и явная победа, что люди беспокоятся об этом.

Ответ 2

Циничный ответ заключается в том, что многие люди программируют на С++, но не понимают и/или не используют функциональность более высокого уровня. Иногда это происходит потому, что они не допускаются, но многие просто не пытаются (или даже понимают).

Как пример невозбуждения: сколько людей используют функциональные возможности, найденные в <algorithm>?

Другими словами, многие программисты на C++ - это просто программисты на C, использующие компиляторы С++ и, возможно, std::vector и std::list. Это одна из причин, по которой использование boost::tuple не является более распространенным.

Ответ 3

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

Рассмотрим:

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

Итак, если вы хотите широко использовать кортежи, вы либо получаете кортеж typedefs всюду, либо повсеместно получаете досадные имена типов. Мне нравятся кортежи. Я использую их, когда это необходимо. Но обычно это ограничивается несколькими ситуациями, например, индексом N-элемента или при использовании мультиплексов для привязки пар итераторов диапазона. И это обычно в очень ограниченном объеме.

Все это очень уродливо и взломанно, когда сравнивается с чем-то вроде Haskell или Python. Когда С++ 0x попадает сюда, и мы получаем ключевые слова "auto", начнем выглядеть намного привлекательнее.

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

Ответ 4

Для меня это привычка, руки вниз: кортежи не решают никаких новых проблем для меня, только некоторые из них я уже могу справиться просто отлично. Обмен значениями по-прежнему чувствует себя более старомодным способом - и, что более важно, я действительно не думаю о том, как "лучше поменять". Это достаточно хорошо, как есть.

Лично я не думаю, что кортежи - отличное решение для возврата нескольких значений - звучит как задание для struct s.

Ответ 5

Но что, если вы хотите повернуть три значения?

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

ОК, поэтому с 4 знаками etc, в конце концов, 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 копий, что делает его совершенно непригодным для некоторых типов (коллекции наиболее очевидны). Не стесняйтесь убеждать меня, что кортежи - хорошая идея для "больших" типов, говоря это не так: -)

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

Ответ 6

Как отмечали многие люди, кортежи просто не так полезны, как другие функции.

  • Переменные и вращающиеся трюки - всего лишь трюки. Они совершенно запутывают тех, кто их раньше не видел, и поскольку это почти все, эти трюки - всего лишь плохая практика разработки программного обеспечения.

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

Ответ 7

Не каждый может использовать boost, и TR1 пока не широко доступен.

Ответ 8

При использовании С++ для встроенных систем вытаскивание библиотек Boost становится сложным. Они соединяются друг с другом, поэтому размер библиотеки растет. Вы возвращаете структуры данных или используете передачу параметров вместо кортежей. При возврате кортежей в Python структура данных находится в порядке и типе возвращаемых значений, просто не явных.

Ответ 9

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

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

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

Ответ 10

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

Скажем, что у нас есть функция "f" , где может возникнуть смысл возврата в кортеж. Как правило, такие функции обычно достаточно сложны, что они могут терпеть неудачу.

Если "f" МОЖЕТ сбой, вам нужно вернуть статус - в конце концов, вы не хотите, чтобы вызывающие абоненты должны были проверять каждый параметр для обнаружения сбоя. "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". Я могу добавить альтернативный "f" возврат, изменив "ReturnInts" без изменения "f" INTERFACE. Решение кортежа не предлагает эту критическую функцию, что делает ее более низким ответом для любого кода библиотеки.

Ответ 11

Поскольку хранилище данных std::tuple имеет худшие характеристики как a struct, так и массива; весь доступ к n-й позиции основан, но нельзя выполнить итерацию через tuple с помощью цикла for.

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

Это оставляет преобразование, упомянутое OP как единственную жизнеспособную usecase для кортежей.

Ответ 12

У меня такое чувство, что многие используют Boost.Any и Boost.Variant(с некоторой техникой) вместо Boost.Tuple.