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

Eme*_*pon 7 c++ stdtuple c++17 structured-bindings

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

结构化绑定

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

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);
Run Code Online (Sandbox Code Playgroud)

我认为这是一种推荐的做法.

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

bol*_*lov 25

我可以这么简单地说:

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

int a,b,c;
std::tie(a,b,c) = function_returning_multiple_values();
Run Code Online (Sandbox Code Playgroud)

是一个黑客:

auto [a, b, c] = function_returning_multiple_values();
Run Code Online (Sandbox Code Playgroud)

正如在假设的世界中,C++只允许一个函数参数

int p1, p2, p3;
p1 = ...;
p2 = ...;
p3 = ...;

function_taking_multiple_params(std::tie_params(p1, p2, p3));
Run Code Online (Sandbox Code Playgroud)

将是一个黑客:

function_taking_multiple_params(p1, p2, p3)
Run Code Online (Sandbox Code Playgroud)

你已经习惯了C++的限制,一个函数最多可以返回一个对象,但事实上它只是一个人为的语言限制,就像接受最多一个参数的限制一样,是一种人为的语言限制.

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

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

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

缺什么?

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

其中,ac已在类型推导b是明确"的std :: string"

  • 嵌套.例如:
auto [a, [b1, b2], c] = foo();
Run Code Online (Sandbox Code Playgroud)

其中第二个返回的对象foo是一个tuple类似的对象.

  • 返回站点的语言功能(绕过std::tuple所有):
auto foo() -> [int, int]
Run Code Online (Sandbox Code Playgroud)

代替

auto foo() -> std::tuple<int, int>
Run Code Online (Sandbox Code Playgroud)
  • 命名返回对象
auto foo() -> [int& key, int& value]
Run Code Online (Sandbox Code Playgroud)

......好吧......不会那么好

  • 并将其与...结合起来 - 为一个很酷的新名称做好准备 - 广义返回初始化:
auto minmax_element(It begin, It end) -> [It min_it, It max_it];

auto [min = *min_it, max = *max_it] = minmax_element(...);
Run Code Online (Sandbox Code Playgroud)

  • 缺少的另一件事是,有时您想在作用域中声明新名称,但有时您实际上想分配给现有变量(或者可能是现有变量和新变量的混合)。奇特的新语法仅声明新变量,而“std::tie”仅使用现有变量。在返回“std::map::emplace”等情况下,混合很有用,其中您可能已经有一个迭代器变量,但没有声明 bool 变量。 (4认同)
  • @EmeraldWeapon:结构化绑定可能是 C++ 所能得到的最好的。 (3认同)
  • @EmeraldWeapon欢迎你.我认为它是一个黑客.这不是语法糖,因为1."拆包"不能做,否则2.有缺点.这就是我看到语法糖的方式.例如,你可以说ranged for是一个语法糖.添加了一个新的语法功能,可以用更少的单词和更清晰的方式执行以前使用更糟糕的语法完成的操作.如果没有结构化绑定,有些事情是你无法做到的.例如,初始化非默认的可构造对象或引用. (3认同)

Sud*_*ire 8

一个非常明显的区别是 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
Run Code Online (Sandbox Code Playgroud)


Hay*_*ayt 5

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);
}
Run Code Online (Sandbox Code Playgroud)

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

现在,因为它们保存了引用,您可以“破解”它来执行类似的操作

int a,b,c;
std::tie(a,b,c) = std::make_tuple(1,2,3);
Run Code Online (Sandbox Code Playgroud)

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

这甚至在刚刚提到的 cpprefence 上作为“注释”

std::tie 可用于解压 std::pair,因为 std::tuple 具有来自对的转换赋值

在 c++17 中,他们引入了“结构化绑定”来处理同时分配多个变量的情况。所以无论是故意的还是黑客行为,从 c++17 开始,这种 tie 的用法应该不再是必要的了。

无论std::tie是打算以这种方式使用还是“黑客”可能都是个人意见,我想介绍的人std::tie最了解这一点。但考虑到在这种情况下结构化绑定是如何替代的std::tie,他们提出了一个他们认为更好的解决方案。