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改变ab?我发现这更有趣,因为它是库功能,而不是语言功能,因此可以肯定的是,我们可以自己实现并了解它。

Answers:


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
  • 元组具有一个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

下一步是确切了解这些结构内部发生的情况。为此,我Tstd::tuple<int>和创建了2种类型的取代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
}

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


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必须是右值引用或const左值引用。您不能将左值引用绑定到prvalue(临时)。尽管这已成为MSVC的“扩展”已有很长时间了。
弗里德曼

1
可能还值得一提的是,与tie结构化绑定不同的是,可以在无法默认构造的类型上使用结构化绑定。

5
是的,std::tie()自C ++ 17以来,它的用处要少得多,在C ++ 17中,结构化绑定通常是更好的选择,但它仍具有用途,包括分配给现有(不是同时新声明的)变量,以及简洁地做其他事情,例如交换多个变量或其他必须分配给参考。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.