Структурированная привязка для замены std::tie abuse

Прочитав это краткое изложение заключительных возможностей C++17, я был немного удивлен разделом о структурированных привязках (выделено мое):

структурированные привязки

До сих пор был известен трюк с использованием std::tie для непосредственного присвоения кортежа или пары различным переменным, вместо того, чтобы иметь дело с типом результата вручную. Это был хак, а также переменные должны были существовать, теперь вы можете объявить переменные и инициализировать их в одну строку:

auto [a, b, c] = getvalues ​​();

Скобки нужны, getvalues ​​возвращает кортеж. std::pair не упоминается в предложении, поэтому неясно, работает ли это с парой, которая возвращается STL в некоторых методах вставки.

Я предполагаю, что они относятся к такого рода использованию std::tie

int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);

который я считал рекомендуемой практикой.

Может кто-нибудь предложить объяснение, почему они ссылаются на приведенный выше пример как взломать?

4 ответа

Решение

Я могу сказать это просто так:

На языке, где функции могут возвращать только одну переменную

int a,b,c;
std::tie(a,b,c) = function_returning_multiple_values();

это взлом для:

auto [a, b, c] = function_returning_multiple_values();

так же, как в гипотетическом мире, где C++ допускает только один параметр для функций

int p1, p2, p3;
p1 = ...;
p2 = ...;
p3 = ...;

function_taking_multiple_params(std::tie_params(p1, p2, p3));

будет взломать для:

function_taking_multiple_params(p1, p2, p3)

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

std::tie библиотека взломать отсутствующую языковую функцию И у него есть некоторые недостатки:

  • переменные должны быть объявлены заранее
  • типы переменных должны быть объявлены явно
  • Неэффективно или не может использоваться с типами, которые не могут быть использованы по умолчанию

Являются ли структурированные привязки всем, чем они могли быть? Нет, но в большинстве случаев это все, что нам нужно.

Чего не хватает?

  • Явный тип для некоторых элементов: например:
auto [a, std::string b, c] = foo();

где a а также c иметь тип выведен и b явно "std::string"

  • Верстка. Например:
auto [a, [b1, b2], c] = foo();

откуда второй возвращаемый объект foo это tuple как объект.

  • Языковая функция на обратном сайте (в обход std::tuple все вместе):
auto foo() -> [int, int]

вместо

auto foo() -> std::tuple<int, int>
  • Именованные возвращаемые объекты
auto foo() -> [int& key, int& value]

... ну... разве это не было бы хорошо

  • и объедините это с... - будьте готовы к новому крутому имени - Обобщенная инициализация возврата:
auto minmax_element(It begin, It end) -> [It min_it, It max_it];

auto [min = *min_it, max = *max_it] = minmax_element(...);

Очень заметная разница - std::ignore. Посмотрите на пример

std::tuple<string, string> data {"Lord", "Buddha"};
auto [a, b] = data; //valid
auto [ , b] = data; //not valid as the identifier is strongly required
string y;
std::tie( std::ignore, y ) = data; //voila

std::tie сам по себе имеет другой функционал.

Он был предназначен для создания кортежа со ссылками на переменные

Создает кортеж из lvalue ссылок на его аргументы или экземпляры std::ignore.

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

bool operator<(const S& rhs) const
{
    // compares n to rhs.n,
    // then s to rhs.s,
    // then d to rhs.d
    return std::tie(n, s, d) < std::tie(rhs.n, rhs.s, rhs.d);
}

Здесь создаются кортежи, но они не копируют переменные, но имеют ссылки.

Теперь, потому что они содержат ссылки, вы можете взломать его, чтобы сделать что-то вроде этого

int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);

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

Это даже на cpprefence только что упоминается как "заметка"

std:: tie можно использовать для распаковки std::pair, потому что std::tuple имеет присваиваемое преобразование из пар

Таким образом, в C++11 не был официальным способом прямого назначения значений, но std::tie может быть использован как это, но, возможно, никогда не был предназначен для использования таким образом.

Поэтому они ввели новую "структурированную привязку".

Будь то std::tie предполагалось использовать таким образом, или это "взлом" может быть личным мнением, я думаю, что люди, которые представили std::tie лучше знать для этого. Но, учитывая, как структурированный вид обязывает заменить std::tie в этом случае они нашли решение, которое они считают лучшим.

Я надеюсь, что никто не возражает, если я внесу свое мнение в микс, потому что я думаю, что оно все еще актуально. Я согласен со многим из сказанного, но не думаю, что структурированные привязки заменят std::tie. Существует особый вариант использования std::tie , который вы просто не можете использовать со структурированными привязками, поскольку переменные объявляются на сайте. Не поймите меня неправильно, я фанат структурированных привязок, но недавно я столкнулся со случаем, когда они просто не подходили. У меня была такая структура...

      std::vector<std::tuple<std::string, uint32_t, uint64_t>> values;

typedef struct
{
    std::string s;
    uint32_t o;
    uint64_t v;
} A;

std::vector<A> my_objs;

Итак, у меня был вектор кортежей и вектор объектов, и я хочу взять значения из кортежей и присвоить эти значения каждому из существующих объектов в векторе, как показано ниже:

      // (This is a very contrived example and you should assume that the
// objects in my_obj are much more complex, already exist and you just want
// to set some values in them)

for (size_t i = 0; i < my_obj.size(); i++)
    std::tie(my_objs.at(i).s, my_objs.at(i).o, my_objs.at(i).v) = values.at(i);

// Sure, you could create a method for my_obj that takes the values, but that's a very
// heavy handed approach and missing the point.

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

      std::unordered_map<std::string, Item*> items;

std::for_each(items.begin(), items.end(), [](const auto&[s, item]) { delete item; });   // Not allowed, you have to do...
std::for_each(items.begin(), items.end(), [](const auto& item_pair) { delete item_pair.second; });   // Acceptable

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

Другие вопросы по тегам