Как работает 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 77

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