29

C++17 标准引入了一个新的结构化绑定特性,该特性最初是在 2015 年提出的,其语法外观被广泛讨论

浏览文档后,您会立即想到它们的一些用途。

聚合分解

让我们声明一个元组:

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;

所以我们可以使用所有成员都是公共的数组或结构/类。

多个返回值

一种从函数中获取多个返回值的便捷方法紧随其后。

还有什么?

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

笔记

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

4

4 回答 4

24

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

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

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

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


@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;
    }
}
于 2017-08-03T10:04:47.490 回答
10

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

They can be used to implement get<N> for structs - see magic_get's automatically generated core17_generated.hpp. This is useful because it provides a primitive form of static reflection (e.g. iterate over all members of a struct).

于 2017-08-03T10:10:10.503 回答
2

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

if (auto&& [a, b] = std::pair { std::string { "how" }, 4U }; a.length() < b)
   std::cout << (a += " convenient!") << '\n';
于 2020-10-17T21:15:51.273 回答
2

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

所以,而不是

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 合着的,他通常不以引入适用性如此狭窄的特性而闻名。

于 2017-10-20T16:39:48.920 回答