5

为什么通过唯一命名的变量和所有模糊的“名称绑定到”语言来定义结构化绑定?

我个人认为结构化绑定的工作方式如下。给定一个结构:

struct Bla
{
    int i;
    short& s;
    double* d;
} bla;

以下:

cv-auto ref-operator [a, b, c] = bla;

(大致)相当于

cv-auto ref-operator a = bla.i;
cv-auto ref-operator b = bla.s;
cv-auto ref-operator c = bla.d;

以及数组和元组的等效扩展。但显然,这太简单了,而且所有这些模糊的特殊语言都用来描述需要发生的事情。

所以我显然遗漏了一些东西,但是在折叠表达式的意义上定义明确的扩展的确切情况是什么,在标准语言中阅读起来要简单得多?

似乎结构化绑定定义的变量的所有其他行为实际上都遵循我认为将用于定义概念的好像简单扩展“规则”。

4

2 回答 2

6

存在结构化绑定以允许在不允许函数解析为多个值的语言中使用多个返回值(因此不会干扰 C++ ABI)。这意味着无论使用什么语法,编译器最终都必须存储实际的返回值。因此,该语法需要一种方法来准确地讨论您将如何存储该值。由于 C++ 在存储事物的方式(作为引用或作为值)方面具有一定的灵活性,因此结构化绑定语法需要提供相同的灵活性。

因此,auto &auto&&auto选择适用于主值而不是子对象。

其次,我们不想影响此功能的性能。这意味着引入的名称永远不会是主对象的子对象的副本。它们必须是引用或实际的子对象本身。这样,人们就不必担心使用结构化绑定对性能的影响;它是纯语法糖。

第三,该系统旨在处理用户定义的对象和具有所有公共成员的数组/结构。在用户定义对象的情况下,“名称绑定到”真正的语言引用,调用get<I>(value). 如果您const auto&为对象存储 a ,那么value将是const&该对象的 a ,并且get可能会返回 a const&

对于数组/公共结构,“名称绑定到”不是引用的东西。它们的处理方式与您键入value[2]value.member_name. 对此类名称执行decltype操作不会返回引用,除非解压缩的成员本身是引用。

通过这种方式,结构化绑定仍然是纯语法糖:它以对该对象最有效的方式访问该对象。对于用户定义的类型,get每个子对象只调用一次并存储对结果的引用。对于其他类型,使用的名称类似于数组/成员选择器。

于 2018-04-12T13:42:54.110 回答
5

似乎结构化绑定定义的变量的所有其他行为实际上都遵循我认为将用于定义概念的好像简单扩展“规则”。

确实如此。除了扩展不是基于右侧的表达式,它是基于引入的变量。这实际上非常重要:

X foo() {
    /* a lot of really expensive work here */
   return {a, b, c};
}

auto&& [a, b, c] = foo();

如果扩展为:

// note, this isn't actually auto&&, but for the purposes of this example, let's simplify
auto&& a = foo().a;
auto&& b = foo().b;
auto&& c = foo().c;

它不仅效率极低,而且在许多情况下也可能是错误的。例如,想象一下如果foo()被实现为:

X foo() {
    X x;
    std::cin >> x.a >> x.b >> x.c;
    return x;
}

因此,它扩展为:

auto&& e = foo();
auto&& a = e.a;
auto&& b = e.b;
auto&& c = e.c;

这确实是确保我们所有的绑定都来自同一个对象而没有任何额外开销的唯一方法。

以及数组和元组的等效扩展。但显然,这太简单了,而且所有这些模糊的特殊语言都用来描述需要发生的事情。

分三种情况:

  1. 数组。每个绑定都好像是对适当索引的访问一样。
  2. 元组状。每个绑定都来自对std::get<I>.
  3. 聚合样。每个绑定命名一个成员。

这不是太糟糕了吗?假设,#1 和#2 可以组合(可以将元组机制添加到原始数组),但是这样做可能更有效。

措辞(IMO)中相当多的复杂性来自处理价值类别。但是无论指定其他任何方式,您都需要它。

于 2018-04-12T14:06:16.633 回答