Як працює std :: tie?


120

Я використовував, 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? Мені це здається цікавішим, оскільки це бібліотечна функція, а не мовна, тому, безумовно, це те, що ми можемо реалізувати і зрозуміти.

Відповіді:


152

Щоб уточнити основну концепцію, зведемо її до більш базового прикладу. Хоча 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.
  • tuple має operator=приймальний кортеж різних типів (але однаковий номер), де кожен член призначається індивідуально - з cppreference :

    template< class... UTypes >
    tuple& operator=( const tuple<UTypes...>& other );

    (3) Для всіх я, правонаступників std::get<i>(other)до std::get<i>(*this).

Наступним кроком є ​​позбавлення від тих функцій, які лише перешкоджають вам, і ми можемо перетворити наш код на це:

int a;
std::tuple<int&>{a} = std::tuple<int>{24};
return a; // 24

Наступний крок - точно зрозуміти, що відбувається всередині цих структур. Для цього я створюю два типи 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.


1
Що мене помиляє, це те, що ми викликаємо оператора присвоєння оцінці.
Адам Захран

У цій відповіді зазначено, що контейнер не може містити посилання. Чому tupleможна було мати довідку?
nn0p

6
@ nn0p std::tuple- це не контейнер, принаймні, не в термінології C ++, не такий, як std::vectorі подібні. Наприклад, ви не можете повторити звичайний шлях по кортежу, оскільки він містить різні типи об'єктів.
болов

@Adam tie (x, y) = make_pair (1,2); насправді стає std :: tie (x, y) .operator = (std :: make_pair (1, 2)), тому "присвоєння рецензії" працює XD
Ju Piece

30

Це жодним чином не відповідає на ваше запитання, але дозвольте мені все-таки опублікувати його, оскільки 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

2
Якщо останній рядок збирається, я трохи стурбований. Це виглядає як прив'язка посилання на тимчасове, що є незаконним.
Нір Фрідман

3
@Neil Це має бути або посиланням на rvalue, або посиланням на const lvalue. Ви не можете прив'язувати посилання lvalue до prvalue (тимчасового). Хоча це було "розширенням" в MSVC протягом століть.
Нір Фрідман

1
Напевно, також варто згадати, що на відміну від цього tie, структуровані прив'язки можуть використовуватися таким чином для типів, які не можуть бути сконструйовані за замовчуванням.
Дан

5
Так, std::tie()набагато менш корисний, оскільки C ++ 17, де структуровані прив’язки зазвичай є вищими, але він все ще використовує, включаючи призначення існуючих (не одночасно щойно оголошених) змінних і стисло виконуючи інші речі, такі як заміна декількох змінних чи інших речей, які повинні призначити посилання.
підкреслюй_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.