Как работает std:: tie?
я использовал std::tie
не придавая этому особого значения. Это работает, поэтому я просто принял это:
auto test()
{
int a, b;
std::tie(a, b) = std::make_tuple(2, 3);
// a is now 2, b is now 3
return a + b; // 5
}
но как это черная магия работы? Как делает временный созданный std::tie
изменить a
и b
? Я нахожу это более интересным, поскольку это библиотечная функция, а не языковая функция, поэтому, безусловно, это то, что мы можем реализовать сами и понять.
2 ответа:
чтобы прояснить основную концепцию, давайте сведем ее к более основному примеру. Хотя
std::tie
полезно для функций, возвращающих (кортеж) значений, мы можем понять его только с одним значением:int a; std::tie(a) = std::make_tuple(24); return a; // 24
вещи, которые мы должны знать, чтобы идти вперед:
std::tie
создает и возвращает набор ссылок.std::tuple<int>
иstd::tuple<int&>
2 совершенно разных класса, без связи между ними, другие чтобы они были сгенерированы из одного шаблона,std::tuple
.Кортеж имеет
operator=
прием кортежа разных типов (но с одинаковым номером), где каждому члену присваивается индивидуально-от cppreference:template< class... UTypes > tuple& operator=( const tuple<UTypes...>& other );
(3) для всех i, присваивает
std::get<i>(other)
доstd::get<i>(*this)
.следующий шаг-избавиться от тех функций, которые только мешают вам, поэтому мы можем преобразовать наш код в это:
int a; std::tuple<int&>{a} = std::tuple<int>{24}; return a; // 24
следующий шаг-точно увидеть, что происходит внутри этих структур. Для этого я создаю 2 типа
T
заместителя дляstd::tuple<int>
иTr
заместителяstd::tuple<int&>
, урезанная до минимума для наших операций:struct T { // substituent for std::tuple<int> int x; }; struct Tr { // substituent for std::tuple<int&> int& xr; auto operator=(const T& other) { // std::get<I>(*this) = std::get<I>(other); xr = other.x; } }; auto foo() { int a; Tr{a} = T{24}; return a; // 24 }
и, наконец, мне нравится избавляться от структур все вместе (ну, это не 100% эквивалент, но это достаточно близко для нас, и достаточно ясно, чтобы позволить это):
auto foo() { int a; { // block substituent for temporary variables // Tr{a} int& tr_xr = a; // T{24} int t_x = 24; // = (asignement) tr_xr = t_x; } return a; // 24 }
так что в основном,
std::tie(a)
инициализирует ссылку на элемент данныхa
.std::tuple<int>(24)
создает элемент данных со значением24
, и присваивание присваивает 24 ссылке на элемент данных в первой структуре. Но поскольку этот элемент данных является ссылкой, привязанной кa
, что в основном назначает24
доa
.
это никак не отвечает на ваш вопрос, но позвольте мне опубликовать его в любом случае, потому что C++17 в основном готов (с поддержкой компилятора), поэтому, задаваясь вопросом, как работает устаревший материал, вероятно, стоит посмотреть, как работает текущая и будущая версия C++.
С C++17 вы можете в значительной степени поцарапать
std::tie
в пользу того, что называется структурированные привязки. Они делают то же самое (ну, не то же самое, но они имеют тот же чистый эффект), хотя вам нужно ввести меньше символов, он не нуждается в поддержке библиотеки, и вы и есть возможность взять ссылки, если это то, что вы хотите.(обратите внимание, что в C++17 конструкторы делают вывод аргументов, поэтому
make_tuple
тоже стало несколько лишним.)int a, b; std::tie(a, b) = std::make_tuple(2, 3); // C++17 auto [c, d] = std::make_tuple(4, 5); auto [e, f] = std::tuple(6, 7); std::tuple t(8,9); auto& [g, h] = t; // not possible with std::tie