Каковы варианты использования для структурированных привязок?

Стандарт C++17 вводит новую функцию структурированных привязок, которая была первоначально предложена в 2015 году и чей синтаксический вид широко обсуждался позже.

Некоторые варианты их использования приходят на ум, как только вы просматриваете документацию.

Агрегаты разложения

Давайте объявим кортеж:

std::tuple<int, std::string> t(42, "foo");

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

auto [i, s] = t;

что эквивалентно:

auto i = std::get<0>(t);
auto s = std::get<1>(t);

или же

int i;
std::string s;
std::tie(i, s) = t;

Ссылки на элементы кортежа также можно получить безболезненно:

auto& [ir, sr] = t;
const auto& [icr, scr] = t;

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

Несколько возвращаемых значений

Удобный способ получить несколько возвращаемых значений из функции немедленно следует из вышесказанного.

Что-то еще?

Можете ли вы предоставить некоторые другие, возможно, менее очевидные варианты использования для структурированных привязок? Как еще они могут улучшить читаемость или даже производительность кода C++?

Заметки

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

3 ответа

Решение

Можете ли вы предоставить некоторые другие, возможно, менее очевидные варианты использования для структурированных привязок? Как еще они могут улучшить читаемость или даже производительность кода C++?

В общем, вы можете использовать его для (позвольте мне сказать) распаковать структуру и заполнить из нее набор переменных:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto [ x, y ] = s;
    (void)x, void(y);
}

Наоборот, было бы:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto x = s.x;
    auto y = s.y;
    (void)x, void(y);
}

То же самое возможно с массивами:

int main() {
    const int a[2] = { 0, 1 };
    auto [ x, y ] = a;
    (void)x, void(y);
}

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


Другой хороший пример, упомянутый в комментариях к ответу @TobiasRibizel, - это возможность перебирать контейнеры и легко распаковывать содержимое.
В качестве примера на основе std::map:

#include <map>
#include <iostream>

int main() {
    std::map<int, int> m = {{ 0, 1 }, { 2, 3 }};
    for(auto &[key, value]: m) {
        std::cout << key << ": " << value << std::endl;
    }
}

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

Они могут быть использованы для реализации get<N> для структур - см. magic_get автоматически генерируется core17_generated.hpp, Это полезно, потому что обеспечивает примитивную форму статического отражения (например, итерацию по всем элементам struct )

За исключением доказательств обратного, я думаю, что структурированные привязки - это просто средство для работы с устаревшими API. ИМХО, API, которые требуют SB, должны были быть исправлены вместо этого.

Итак, вместо

auto p = map.equal_range(k);
for (auto it = p.first; it != p.second; ++it)
    doSomethingWith(it->first, it->second);

мы должны быть в состоянии написать

for (auto &e : map.equal_range(k))
    doSomethingWith(e.key, e.value);

Вместо

auto r = map.insert({k, v});
if (!r.second)
    *r.first = v;

мы должны быть в состоянии написать

auto r = map.insert({k, v});
if (!r)
    r = v;

и т.п.

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

Инициализация нескольких переменных разных типов в операторе if; например,

if (auto&& [a, b] = std::pair { std::string { "how" }, 4U }; a.length() < b)
   std::cout << (a += " convenient!") << '\n';
Другие вопросы по тегам