结构化绑定取代std :: tie滥用

时间:2016-10-25 13:29:51

标签: c++ c++17 stdtuple structured-bindings

在阅读this关于c ++ 17最终特性的总结时,我对结构化绑定(强调我的)部分感到有些惊讶:

  

结构化绑定

     

到目前为止,有一种已知的技巧滥用std :: tie直接为不同的变量分配元组或对,而不必手动处理结果类型。 这是一个黑客,并且变量必须存在,现在您可以声明变量并将它们初始化为一行:

     

auto [a,b,c] = getvalues();

     

需要大括号,getvalues返回一个元组。提案中没有提到std :: pair,因此不清楚它是否适用于在某些插入方法中由STL返回的pair。

我假设他们引用std::tie

的这种用法
int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);

我认为这是推荐的做法。

有人可以解释为什么他们将上述示例称为黑客攻击吗?

4 个答案:

答案 0 :(得分:23)

我可以简单地说出来:

在一种语言中,函数只能返回一个变量

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 ++的限制,一个函数最多可以返回一个对象,但事实上它只是一个人为的语言限制,就像接受最多一个参数的限制一样,是一种人为的语言限制。 / p>

std::tie是一个缺少语言功能的库黑客。它有一些缺点:

  • 需要事先声明变量
  • 必须明确声明变量类型
  • 效率低或不能用于非默认可构造的类型

结构化绑定是他们本来可以做的一切吗?不,但在大多数情况下,它们都是我们需要的一切。

缺少什么?

  • 某些元素的显式类型:例如:
auto [a, std::string b, c] = foo();

其中ac的推断类型为b,显式为“std :: string”

  • 嵌套。 E.g:
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(...);

答案 1 :(得分:4)

一个非常明显的区别是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

答案 2 :(得分:1)

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);
}

这里创建了元组,但它们不会复制变量但是有引用。

现在因为他们持有参考资料你可以&#34; hack&#34;它可以做这样的事情

int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);

它将返回的元组的值分配给具有引用的元素。

这甚至是刚刚提到的cpprefence&#34;注意&#34;

  

std :: tie可用于解包std :: pair,因为std :: tuple具有转换分配

所以在c ++ 11中,实际上并不是直接赋值的官方方式,但std::tie可以用作这个,但也许从来没有打算用这种方式。

所以他们引入了新的&#34;结构化绑定&#34;。

std::tie是否意味着以这种方式使用,或者是&#34; hack&#34;可能是个人意见,我想介绍std::tie的人最了解这一点。但考虑到结构化绑定在这种情况下如何取代std::tie,他们提出了一个他们认为更好的解决方案。

答案 3 :(得分:0)

我希望没有人介意我把我的意见混在一起,因为我认为它仍然有效。我同意很多说法,但我认为结构化绑定不会取代 std::tiestd::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 仍然有一个非常有用的目的,即结构化绑定绑定可以提供,但不能。