【问题标题】:What are use cases for structured bindings?结构化绑定的用例是什么?
【发布时间】:2018-01-10 20:36:50
【问题描述】:

C++17 标准引入了一个新的structured bindings 功能,最初是 2015 年的proposed,后来它的语法出现了广泛的discussed

当您查看文档时,就会想到它们的一些用途。

聚合分解

让我们声明一个元组:

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++ 代码的可读性甚至性能?

备注

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

【问题讨论】:

  • 已经阅读了答案......基本上没有。这只是语法糖。由于您不能使它们成为可变参数,因此围绕可变参数模板扩展也没有什么有趣或令人印象深刻的事情。
  • 我看到的结构化绑定的唯一问题是不能“忽略”聚合的任何成员。如果将此与 Python 的可迭代解包进行对比,它似乎没那么有用。
  • @Sergey 这只不过是一种在一组变量中解压缩结构的方法。实际上并没有那么有趣或令人印象深刻。
  • 您只想列出语言功能的用途?这就是“太宽泛”的缩影。
  • 同意这个问题可以更具体,但如果措辞更简洁,可能不会太宽泛。我要指出,从前人们也没有过多考虑设计模式。规范中是否有某些内容表明需要添加结构化绑定?

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


【解决方案1】:

您能否提供一些其他可能不太明显的结构化绑定用例?它们还能如何提高 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);
}

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


cmets 中提到的另一个很好的例子是@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;
    }
}

【讨论】:

  • 您还可以在迭代元组或映射(键值对)的集合时添加使用以生成更完整的列表
  • 好点。让我创建一个最小的示例来添加到答案中。谢谢。
【解决方案2】:

您能否提供一些其他可能不太明显的结构化绑定用例?

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

【讨论】:

  • 天哪,这看起来很可恶。
  • 虽然上下滚动很有趣。
【解决方案3】:

除非有相反的证据,我认为结构化绑定只是处理遗留 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 共同撰写的,他通常不以介绍适用性如此狭窄的特性而闻名。

【讨论】:

  • C++17 用户可以从现有 API 中获得更整洁的代码,这些代码注定要慢慢改变,无需更改它并强加给那些已经对所有 pair 感到满意(!)的人进行重写恐怖。只要我们坚持使用现有的 API,我很高兴能够整理这些代码。它也更接近(看起来)用户函数的多个返回值(使用比pair更好的类型);那些人的缺席从来没有让我太困扰,但我觉得这是其他人非常热衷的事情。当然,编译器作者可能比我们更赞同你!
  • fwiw,imho if (!r) *r = v; 会更清楚,而 imho if (!r) r = v; 会令人困惑。
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 2018-09-23
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 2011-02-25
    相关资源
    最近更新 更多