web-dev-qa-db-ja.com

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をどのように変更しますか?これは言語の機能ではなくライブラリの機能であるため、私はこれがより興味深いと感じています。

95
bolov

コアの概念を明確にするために、より基本的な例に減らしましょう。 std::tieは、より多くの値(のタプル)を返す関数には便利ですが、たった1つの値で問題なく理解できます。

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

次のステップは、これらの構造内で何が起こるかを正確に確認することです。このために、std::Tuple<int>T置換基std::Tuple<int&>の2種類のTr置換基を作成します。

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にバインドされた参照であるため、基本的に24aに割り当てます。

125
bolov

これはあなたの質問には決して答えませんが、C++ 17は基本的に(コンパイラサポート付き)準備ができているので、それを投稿させてください。将来的には、C++のバージョンも機能します。

C++ 17では、いわゆる構造化バインディングを優先してstd::tieをスクラッチできます。同じことを行います(まあ、同じではありません、しかし同じ効果があります)ライブラリのサポート、およびalso参照が必要な場合は、参照を取得することができます.

(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
24
Damon