【问题标题】:Why does structured binding introduce variables as values, not references?为什么结构化绑定将变量作为值而不是引用引入?
【发布时间】:2020-04-21 09:47:24
【问题描述】:

我正在学习结构化绑定声明。我的理解是,在auto& [x, y] = expr; 中,变量xy 被引入类型“引用std::tuple_element<i, E>::type”(对于i=0, 1E 是不可见变量e 的类型)。而且,这些变量都是用get<i>(e)初始化的。

所以,如果我使用 auto& 并且 get<> 返回一个值(不是引用),它不应该编译,因为您不能将左值绑定到临时值。但是,以下示例在 GCC、Clang 和 Visual Studio 的某些版本中为我构建:

#include <cstddef>
#include <tuple>
#include <type_traits>

struct Foo {
    template<std::size_t i>
    int get() { return 123; }
};

namespace std {
    template<> struct tuple_size<Foo> : integral_constant<size_t, 1> {};
    template<std::size_t i> struct tuple_element<i, Foo> { using type = int; };
}

int main() {
    Foo f;
    auto& [x] = f;
    x++;
}

此外,C++ Insights 清楚地表明,clang 将结构化绑定扩展为:

Foo f = Foo();
Foo & __f17 = f;
std::tuple_element<0, Foo>::type x = __f17.get<0>();
x++;

在这里,它声明x 不是作为引用,而是作为值。这是为什么呢?

我预计左值引用和编译错误:e(上例中的__f17)是左值引用。

【问题讨论】:

    标签: c++ structured-bindings


    【解决方案1】:

    这是因为auto&amp; 不适用于结构化绑定。它应用于引用该结构的基础实体。在您的 cppinsights sn-p 中,这将是 __f17

    如果你改用auto [x],sn-p 会扩展成这样的东西

    Foo f = Foo();
    Foo __f17 = f; // Difference here
    std::tuple_element<0, Foo>::type x = __f17.get<0>();
    x++;
    

    绑定本身总是一种对底层对象的引用。但是,cppinsights 代码并不能准确地表示这一点。 C++标准中的相关段落是这么说的

    [dcl.struct.bind]

    3 否则,如果限定 ID std​::​tuple_­size&lt;E&gt; 命名为 完整类型,表达式std​::​tuple_­size&lt;E&gt;​::​value 应 是一个格式良好的整数常数表达式和个数 标识符列表中的元素应等于该值 表达。 unqualified-id getE 的范围内被查找 类成员访问查找,如果找到至少一个 声明,初始化程序是e.get&lt;i&gt;()。否则,该 初始值设定项为get&lt;i&gt;(e),其中 get 在关联中查找 命名空间。在任何一种情况下,get&lt;i&gt; 都被解释为模板 ID。 [ 注意:不执行普通的非限定查找。 — 尾注 ] 在 无论哪种情况,如果实体 e 的类型是左值,则 e 是左值 参考和 xvalue 否则。给定类型Ti std​::​tuple_­element&lt;i, E&gt;​::​type,每个vi是一个变量类型 “引用Ti”用初始化器初始化,其中 如果初始值设定项是左值并且引用是左值引用 否则为右值引用;引用的类型是Ti

    【讨论】:

    • 虽然这是有道理的,但引用的文本不是指出vi 必须是一个引用(对于这个底层e 中的某些东西,无论它是否是一个引用)?
    • @LapshinDmitry - 确实如此。就像我提到的那样,CPP 洞察力并不能正确地代表这一点。
    • 如果绑定是对底层对象的引用,它们如何绑定到临时对象?它们应该是左值引用,因为__f17 是左值。
    • __f17 的左值性与此无关。绑定的初始值设定项是__f17.get&lt;0&gt;(),这是一个右值,所以引用类型是一个右值引用(文中明确说明)。至于对临时对象的绑定引用,您是否熟悉reference lifetime extension?就是这样。
    • @StoryTeller-UnslanderMonica 好的,谢谢!但是我仍然没有得到任何东西:我已经构建了一个不同的示例,该示例返回来自 get 的引用,所以我应该获得引用(并且它适用于我放置的断言),但 decltype 报告我没有这些:@987654323 @
    猜你喜欢
    • 2023-03-10
    • 2011-08-15
    • 2015-12-13
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多