结构化绑定取代std::tie滥用
Structured binding to replace std::tie abuse
在阅读c++17最终特性的总结时,我对结构化绑定部分(强调我的)感到有点惊讶:
结构化绑定到目前为止,有一个已知的技巧是滥用std::tie直接将元组或对赋值给不同的变量,而不必手动处理结果类型。这是一个hack,并且变量必须存在,现在您可以在一行中声明变量并初始化它们:
auto [a, b, c] = getvalues();
需要大括号,getvalues返回一个元组。建议中没有提到std::pair,所以不清楚这是否适用于pair,它是由STL在某些插入方法中返回的。
我假设他们指的是std::tie
的这种用法
int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);
我认为这是一种推荐的做法。
有人能解释一下为什么他们把上面的例子称为黑客吗?
我可以这么简单地说:
函数只能返回一个变量的语言
int a,b,c;
std::tie(a,b,c) = function_returning_multiple_values();
是对
的改进:auto [a, b, c] = function_returning_multiple_values();
就像假设c++只允许函数有一个形参
int p1, p2, p3;
p1 = ...;
p2 = ...;
p3 = ...;
function_taking_multiple_params(std::tie_params(p1, p2, p3));
可以替换为:
function_taking_multiple_params(p1, p2, p3)
你已经习惯了c++中一个函数最多只能返回一个对象的限制,但实际上这只是一种人为的语言限制,就像最多只能接受一个参数的限制是人为的语言限制一样。
std::tie
是为缺少的语言特性编写的库。它也有一些缺点:
- 变量需要事先声明
- 变量类型必须显式声明
- 效率低下或不能用于非默认可构造类型
结构化绑定是它们本来可以做到的一切吗?不,但在大多数情况下,它们是我们所需要的一切。
缺少什么?
- 某些元素的显式类型:例如:
auto [a, std::string b, c] = foo();
其中a
和c
已推导出类型,b
显式为"std::string"
- 嵌套。例如:
auto [a, [b1, b2], c] = foo();
,从foo
返回的第二个对象是一个类似tuple
的对象。
- 返回站点的语言功能(一起绕过
std::tuple
):
auto foo() -> [int, int]
不是auto foo() -> std::tuple<int, int>
- 指定返回对象
auto foo() -> [int& key, int& value]
…嗯…那不是很好吗
- 并将其与…-准备好一个很酷的新名字-广义返回初始化:
auto minmax_element(It begin, It end) -> [It min_it, It max_it];
auto [min = *min_it, max = *max_it] = minmax_element(...);
一个非常明显的区别是std::ignore。看一下示例
std::tuple<string, string> data {"Lord", "Buddha"};
auto [a, b] = data; //valid
auto [ , b] = data; //not valid as the identifier is strongly required
string y;
std::tie( std::ignore, y ) = data; //voila
std::tie
本身还有另一个功能。
用来创建一个引用变量
的元组创建一个由左值引用到其实参或std::ignore实例组成的元组。
这对于创建动态元组很有用,而不必复制变量,因为它们是引用。我只是用cppreference中的例子作为一个用例。
bool operator<(const S& rhs) const
{
// compares n to rhs.n,
// then s to rhs.s,
// then d to rhs.d
return std::tie(n, s, d) < std::tie(rhs.n, rhs.s, rhs.d);
}
这里元组被创建,但它们不复制变量,而是有引用。
现在,因为它们保存引用,你可以"黑"它来做这样的事情
int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);
它将返回的元组的值赋给本身有引用的元组。
这甚至是在cpprevention上刚刚提到的"注释"
std::tie可用于解包std::pair,因为std::tuple具有对
的转换赋值。
在c++17中,他们引入了"结构化绑定"来处理一次性分配多个变量的场景。因此,无论这是有意为之还是黑客所为,从c++17开始,tie的这种用法应该不再必要了。
无论std::tie
是打算这样使用还是"黑客"可能是个人的意见,我想引入std::tie
的人对此最了解。但是考虑到结构化绑定在这种情况下是如何取代std::tie
的,他们想出了一个他们认为更好的解决方案。
我希望没有人介意我提出我的观点,因为我认为它仍然是有效的。我同意上面所说的很多内容,但我不认为结构化绑定会取代std::tie。对于std::tie有一个特定的用例,您根本无法使用结构化绑定,因为变量是在现场声明的。不要误解我的意思,我是结构化绑定的粉丝,但我最近遇到了一个案例,它们就是不合适。我有这样的结构…
std::vector<std::tuple<std::string, uint32_t, uint64_t>> values;
typedef struct
{
std::string s;
uint32_t o;
uint64_t v;
} A;
std::vector<A> my_objs;
好了,我有一个元组的向量和一个对象的向量我要做的就是从元组中取值并将这些值赋给向量中每个现有的对象,如下所示:
// (This is a very contrived example and you should assume that the
// objects in my_obj are much more complex, already exist and you just want
// to set some values in them)
for (size_t i = 0; i < my_obj.size(); i++)
std::tie(my_objs.at(i).s, my_objs.at(i).o, my_objs.at(i).v) = values.at(i);
// Sure, you could create a method for my_obj that takes the values, but that's a very
// heavy handed approach and missing the point.
如果变量不存在,那么结构化绑定是您最好的朋友,但是如果存在,它们就没有帮助。此外,正如有人提到的,结构化绑定中还有许多其他的遗漏,对我来说,这意味着它们缺乏。首先是嵌套它们的能力,例如其中一个变量本身可能是一个元组/对等。其次,虽然这有点不同,但无法在lambda表达式声明符中使用结构化绑定,如:
std::unordered_map<std::string, Item*> items;
std::for_each(items.begin(), items.end(), [](const auto&[s, item]) { delete item; }); // Not allowed, you have to do...
std::for_each(items.begin(), items.end(), [](const auto& item_pair) { delete item_pair.second; }); // Acceptable
我认为对结构化绑定有用的是,声明的变量可以是对现有对象的引用。因此,尽管我觉得有些人认为std::tie将被结构化绑定所取代,但现实情况是,std::tie仍然具有结构化绑定可以提供的非常有用的目的,但只是没有。
- 滥用条件变量
- ios_base::sync_with_stdio(false); cin.tie(NULL);
- 在 std::tie 中使用 std::weak_ptr::lock()
- std::tie 和 std::forward_as_tuple 有什么区别
- 滥用逗号运算符来库里函数参数
- 使用std::tie进行类似golang的错误处理,同时返回结果,是否有缺点?(C++11)
- 尝试实现std::tie和std::tuple的小版本
- 为什么此元组到引用元组 (std::tie) 转换有效?
- 使用标准库在 c++11 中使用 std::tie 提取嵌套在元组中的元组
- 谁能解释一下C++中关于cin.tie()的代码?
- 如何优化最大总和代码以获得 tie<1.276s?
- std::tie 和元组中返回的对象的生存期
- 使用std::tie解包返回值会导致额外的成本吗?
- 模板滥用?
- std::tie 在从函数调用传递值时失败,并显示"无法绑定非常量左值引用"
- 是否有一种很好的方法可以将STD :: Minmax(A,B)分配给STD :: TIE(A,B)
- 使用 C++ 模板定义函数,但显式删除它以防止滥用
- 相同的代码,如果 tie 存在于priority_queue中,则顺序不同,使用 clang 和 gcc
- 在将指针传递到类范围的constexpr函数时,会滥用父模板的参数
- 结构化绑定取代std::tie滥用