Каковы варианты использования для структурированных привязок?
Стандарт 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';