std::tie 是如何工作的?

How does std::tie work?

提问人:bolov 提问时间:5/3/2017 最后编辑:Daniel Kamil Kozarbolov 更新时间:6/18/2017 访问量:86319

问:

我没有考虑太多就使用了。它有效,所以我刚刚接受了这一点: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::tieab

C++ C++11 元组

评论


答:

233赞 bolov 5/3/2017 #1

为了澄清核心概念,让我们将其简化为一个更基本的例子。虽然对于返回(元组)更多值的函数很有用,但我们可以只用一个值来理解它:std::tie

int a;
std::tie(a) = std::make_tuple(24);
return a; // 24

为了继续前进,我们需要知道的事情:

  • std::tie构造并返回引用的元组。
  • std::tuple<int>并且是 2 个完全不同的类,它们之间没有任何联系,只是它们是从同一个模板生成的,.std::tuple<int&>std::tuple
  • 元组接受不同类型的元组(但数量相同),其中每个成员都是单独分配的 - 来自 CPPREFERENCEoperator=

    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 种类型的替代基和替代基,为我们的操作剥离到最低限度:Tstd::tuple<int>Trstd::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
}

因此,基本上,初始化对 . 创建一个值为 的数据成员,赋值将 24 分配给第一个结构中的数据成员引用。但是,由于该数据成员是绑定到 的引用,因此基本上分配给 。std::tie(a)astd::tuple<int>(24)24a24a

评论

2赞 Adham Zahran 4/23/2018
让我烦恼的是,我们正在将赋值运算符调用到右值。
2赞 nn0p 8/13/2019
在这个答案中,它指出容器不能保存引用。为什么可以持有参考?tuple
13赞 bolov 8/13/2019
@nn0p 不是一个容器,至少在C++术语中不是,与 和 等不同。例如,您不能使用通常的方式对元组进行迭代,因为它包含不同类型的对象。std::tuplestd::vector
2赞 Ju Piece 4/23/2020
@Adam 并列(x,y) = make_pair(1,2);实际上变成了 std::tie(x, y).operator=(std::make_pair(1, 2)),这就是“赋值到右值”工作的原因 XD
60赞 Damon 5/4/2017 #2

这并不能以任何方式回答您的问题,但无论如何让我发布它,因为 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赞 Dan 5/5/2017
可能还值得一提的是,与 不同的是,结构化绑定可以以这种方式用于非默认构造的类型。tie
13赞 underscore_d 11/15/2018
是的,自 C++17 以来,它的用处要小得多,其中结构化绑定通常更胜一筹,但它仍然有用途,包括分配给现有的(不是同时新声明的)变量,以及简洁地执行其他操作,例如交换多个变量或其他必须分配给引用的事情。std::tie()
1赞 TRPh 9/2/2021
无法比较结构化绑定。领带可以。
0赞 stackoverblown 10/5/2022
谢谢你的回答!我现在可以永远告别 std::get<int>(...) 调用!调用语法实在是太麻烦了。谁建议的?