我已经使用了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
?我发现这更有趣,因为它是一个库功能,而不是语言功能,所以我们可以自己实现并理解它。
答案 0 :(得分:124)
为了澄清核心概念,让我们将其简化为一个更基本的例子。虽然std::tie
对返回(一个元组)更多值的函数很有用,但我们只用一个值就可以理解它:
int a;
std::tie(a) = std::make_tuple(24);
return a; // 24
为了向前发展我们需要知道的事情:
std::tie
构造并返回一个引用元组。std::tuple<int>
和std::tuple<int&>
是两个完全不同的类,它们之间没有任何关联,另外它们是从同一个模板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
下一步是确切了解这些结构中发生了什么。
为此,我为T
和std::tuple<int>
取代基Tr
创建了2种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 :(得分:22)
这不会以任何方式回答你的问题,但是让我发布它,因为C ++ 17基本上已经准备就绪(有编译器支持),所以在想知道过时的东西是如何工作的时候,可能值得看看如何当前和未来的C ++版本也有效。
使用C ++ 17,您可以使用std::tie
来支持所谓的结构化绑定。他们做同样的事情(好吧,不一样,但它们具有相同的净效果),虽然你需要输入更少的字符,它不需要库支持,你也如果碰巧是你想要的,那么em>有能力参考。
(请注意,在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