Подтвердить что ты не робот

Каковы хорошие варианты использования кортежей в С++ 11?

Каковы хорошие варианты использования кортежей в С++ 11? Например, у меня есть функция, которая определяет локальную структуру следующим образом:

template<typename T, typename CmpF, typename LessF>
void mwquicksort(T *pT, int nitem, const int M, CmpF cmp, LessF less)
{
  struct SI
  {
    int l, r, w;
    SI() {}
    SI(int _l, int _r, int _w) : l(_l), r(_r), w(_w) {}
  } stack[40];

  // etc

Я рассматривал возможность замены структуры SI на std::tuple<int,int,int>, которая является гораздо более коротким объявлением с удобными конструкторами и операторами, уже предопределенными, но со следующими недостатками:

  • Элементы Tuple скрыты в неясных, определенных реализацией структурах. Несмотря на то, что Visual Studio интерпретирует и показывает их содержимое красиво, я все еще не могу установить условные точки останова, которые зависят от значения элементов кортежа.
  • Доступ к отдельным полям кортежа (get<0>(some_tuple)) гораздо более подробный, чем доступ к элементам структуры (s.l).
  • Доступ к полям по имени гораздо более информативен (и короче!), чем числовым индексом.

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

ОБНОВЛЕНИЕ. Оказывается, что отладчик VS2010 SP1 не может отображать содержимое следующего массива std::tuple<int, int, int> stack[40], но он отлично работает, когда он кодируется с помощью структуры. Таким образом, решение в основном не вызывает затруднений: если вам когда-либо придется проверять свои значения, используйте struct [esp. важно с такими отладчиками, как GDB].

4b9b3361

Ответ 1

Ну, imho, самая важная часть - это общий код. Написание генерического кода, который работает на всех типах структур, намного сложнее, чем писать дженерики, которые работают с кортежами. Например, функция std::tie, которую вы упомянули, будет очень трудно сделать для структур.

это позволяет делать такие вещи:

  • Параметры функции сохранения для отложенного выполнения (например, этот вопрос)
  • Возвратить несколько параметров без громоздкой (un) упаковки с помощью std::tie
  • Объединить (не одинаковые) наборы данных (например, от параллельного выполнения), это можно сделать просто как std::tuple_cat.

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

Ответ 2

Это простой способ вернуть несколько значений из функции;

std::tuple<int,int> fun();

Значения результата можно использовать элегантно следующим образом:

int a;
int b;
std::tie(a,b)=fun();

Ответ 3

Я думаю, что большинство из них для tuple происходит от std::tie:

bool MyStruct::operator<(MyStruct const &o) const
{
    return std::tie(a, b, c) < std::tie(o.a, o.b, o.c);
}

Наряду со многими другими примерами в ответах здесь. Однако я считаю этот пример наиболее полезным, поскольку он сэкономит много сил, как он был на С++ 03.

Ответ 4

Вы когда-нибудь использовали std::pair? Многие из мест, которые вы использовали бы std::tuple, схожи, но не ограничиваются точно двумя значениями.

Недостатки, которые вы перечисляете для кортежей, также применимы к std:: pair, иногда вам нужен более выразительный тип с лучшими именами для его членов, чем first и second, но иногда вам это не нужно. То же самое относится к кортежам.

Ответ 5

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

(возможно) сохранение при вводе не компенсирует потери в самодокументирующихся свойствах полученного кода.

Подстановка кортежей для структур, которые просто убирают значащее имя для поля, заменяя имя поля "числом" (точно так же, как непродуманное понятие пары std::).

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

Ответ 6

Реальные случаи использования - это ситуации, когда у вас есть неузнаваемые элементы - вариативные шаблоны и лямбда-функции. В обеих ситуациях вы можете иметь неназванные элементы с неизвестными типами, и таким образом единственным способом их хранения является структура с неназванными элементами: std:: tuple. В любой другой ситуации у вас есть известное количество именных элементов с известными типами и, таким образом, может использовать обычную структуру, которая является лучшим ответом в 99% случаев.

Например, вы не должны использовать std:: tuple, чтобы иметь "множественные возвращения" от обычных функций или шаблонов с фиксированным числом общих входов. Используйте для этого реальную структуру. Реальный объект FAR более "общий", чем std:: tuple cookie-cutter, потому что вы можете дать реальный объект буквально любому интерфейсу. Это также даст вам гораздо больше безопасности и гибкости в публичных библиотеках.

Просто сравните эти 2 функции-члены класса:

std::tuple<double, double, double>  GetLocation() const; // x, y, z

GeoCoordinate  GetLocation() const;

С реальным объектом "геокоордината" я могу предоставить оператор bool(), который возвращает false, если родительский объект не имел местоположения. Через своих API-интерфейсов пользователи могут получить места x, y, z. Но вот большая вещь - если я решила сделать GeoCoordinate 4D, добавив поле времени через 6 месяцев, текущий код пользователя не сломается. Я не могу сделать это с помощью версии std:: tuple.

Ответ 7

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

Ответ 8

Я не могу комментировать ответ mirk, поэтому мне придется дать отдельный ответ:

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

void my_func(const MyClass& input, MyClass& output1, MyClass& output2, MyClass& output3)
{
   // whatever
}

вездесущ в традиционном С++, потому что это единственный способ получить несколько объектов, возвращаемых функцией, это мерзость для функционального программирования. Теперь вы можете написать

tuple<MyClass, MyClass, MyClass> my_func(const MyClass& input)
{
   // whatever
   return tuple<MyClass, MyClass, MyClass>(output1, output2, output3);
}

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