std :: tie是如何工作的?

bol*_*lov 105 c++ tuples c++11

我已经习惯了,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
}
Run Code Online (Sandbox Code Playgroud)

但这个黑魔法是如何运作的呢?如何做一个临时的创建std::tie改变ab?我发现这更有趣,因为它是一个库功能,而不是语言功能,所以我们可以自己实现并理解它.

bol*_*lov 136

为了阐明核心概念,让我们将其简化为一个更基本的例子.虽然std::tie对返回(一个元组)更多值的函数很有用,但我们只需要一个值就能理解它:

int a;
std::tie(a) = std::make_tuple(24);
return a; // 24
Run Code Online (Sandbox Code Playgroud)

为了向前发展我们需要知道的事情:

  • std::tie 构造并返回一个引用元组.
  • std::tuple<int>并且std::tuple<int&>是两个完全不同的类,它们之间没有连接,另外它们是从同一个模板生成的std::tuple.
  • 元组有一个operator=接受不同类型(但相同数字)的元组,其中每个成员都是单独分配的 - 来自cppreference:

    template< class... UTypes >
    tuple& operator=( const tuple<UTypes...>& other );
    
    Run Code Online (Sandbox Code Playgroud)

    (3)对于所有我,分配std::get<i>(other)std::get<i>(*this).

下一步是摆脱那些只会妨碍你的功能,所以我们可以将代码转换为:

int a;
std::tuple<int&>{a} = std::tuple<int>{24};
return a; // 24
Run Code Online (Sandbox Code Playgroud)

下一步是确切了解这些结构中发生了什么.为此,我创建了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
}
Run Code Online (Sandbox Code Playgroud)

最后,我喜欢一起摆脱这些结构(好吧,它不是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
}
Run Code Online (Sandbox Code Playgroud)

所以基本上,std::tie(a)初始化一个数据成员引用a.std::tuple<int>(24)使用值创建数据成员,24赋值为第一个结构中的数据成员引用分配24.但由于该数据成员是一个绑定的引用a,基本上是指定24a.

  • @ nn0p`std :: tuple`不是容器,至少不是C ++术语,与`std :: vector`等不一样。例如,您不能使用元组的常规方法进行迭代,因为它包含不同类型的对象。 (4认同)
  • 让我烦恼的是我们正在调用右值的赋值运算符。 (2认同)
  • 在[this](/sf/answers/280766041/)答案中,它指出容器不能容纳引用。为什么“tuple”可以保存引用? (2认同)
  • @Adam tie(x,y) = make_pair(1,2); 实际上变成了 std::tie(x, y).operator=(std::make_pair(1, 2)),这就是为什么“分配给右值”有效 XD (2认同)

Dam*_*mon 25

这不会以任何方式回答你的问题,但是让我发布它,因为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
Run Code Online (Sandbox Code Playgroud)

  • 是的,`std::tie()` 自 C++17 以来就没有那么有用了,结构化绑定通常更胜一筹,但它仍然有用途,包括分配给现有的(不是同时新声明的)变量和简洁地做其他事情比如交换多个变量或其他必须分配给引用的东西。 (9认同)
  • 如果最后一行编译我有点担心.它看起来像绑定对临时的引用是非法的. (2认同)
  • @Neil它必须是rvalue引用或const lvalue引用.您不能将左值引用绑定到prvalue(临时).虽然这已经成为MSVC的一个"延伸". (2认同)
  • 可能还值得一提的是,与“tie”不同,结构化绑定可以以这种方式用于不可默认构造的类型。 (2认同)