结构化绑定的用例是什么?

What are use cases for structured bindings?

C++17 标准稍后引入了一个新的structured bindings feature, which was initially proposed in 2015 and whose syntactic appearance was widely

您一浏览文档就会想到它们的一些用途。

聚合分解

让我们声明一个元组:

std::tuple<int, std::string> t(42, "foo");

可以通过一行中的结构化绑定轻松获得命名元素副本:

auto [i, s] = t;

相当于:

auto i = std::get<0>(t);
auto s = std::get<1>(t);

int i;
std::string s;
std::tie(i, s) = t;

元组元素的引用也可以轻松获得:

auto& [ir, sr] = t;
const auto& [icr, scr] = t;

所以我们可以使用数组或structs/classes,其所有成员都是public。

多个 return 值

从一个函数中获取多个 return 值的便捷方法紧随上述内容。

还有什么?

您能否提供一些其他可能不太明显的结构化绑定用例?它们还能如何提高 C++ 代码的可读性甚至性能?

备注

正如评论中提到的,结构化绑定的当前实现缺少一些功能。它们是非可变的,并且它们的语法不允许显式跳过聚合成员。 Here 可以找到关于可变性的讨论。

Can you provide some other, possibly less obvious use cases for structured bindings? How else can they improve readability or even performance of C++ code?

更一般地说,您可以使用它来(让我说)解压一个结构并从中填充一组变量:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto [ x, y ] = s;
    (void)x, void(y);
}

反之则为:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto x = s.x;
    auto y = s.y;
    (void)x, void(y);
}

数组也是如此:

int main() {
    const int a[2] = { 0, 1 };
    auto [ x, y ] = a;
    (void)x, void(y);
}

无论如何,因为当您 return 来自函数的结构或数组时它也有效,您可能会争辩说这些示例属于您已经提到的同一组案例。


@TobiasRibizel 对答案的评论中提到的另一个很好的例子是遍历容器并轻松解压内容的可能性。
例如基于 std::map:

#include <map>
#include <iostream>

int main() {
    std::map<int, int> m = {{ 0, 1 }, { 2, 3 }};
    for(auto &[key, value]: m) {
        std::cout << key << ": " << value << std::endl;
    }
}

Can you provide some other, possibly less obvious use cases for structured bindings?

它们可用于为结构实现 get<N> - 参见 magic_get's automatically generated core17_generated.hpp。这很有用,因为它提供了静态反射的原始形式 (例如遍历 struct 的所有成员).

除非有相反的证据,否则我认为结构化绑定只是处理遗留问题的一种工具 API。恕我直言,需要 SB 的 APIs 应该已经修复。

所以,而不是

auto p = map.equal_range(k);
for (auto it = p.first; it != p.second; ++it)
    doSomethingWith(it->first, it->second);

我们应该可以写

for (auto &e : map.equal_range(k))
    doSomethingWith(e.key, e.value);

而不是

auto r = map.insert({k, v});
if (!r.second)
    *r.first = v;

我们应该可以写

auto r = map.insert({k, v});
if (!r)
    r = v;

等等

当然,有人会在某个时候找到巧妙的用途,但对我来说,在了解它们一年之后,它们仍然是一个未解之谜。特别是因为这篇论文是由 Bjarne 合着的,Bjarne 通常不以引入具有如此狭窄适用性的功能而闻名。

在if语句中初始化多个不同类型的变量;例如,

if (auto&& [a, b] = std::pair { std::string { "how" }, 4U }; a.length() < b)
   std::cout << (a += " convenient!") << '\n';