1

我正在学习结构化绑定声明。我的理解是,在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上面的示例中)是左值引用。

4

1 回答 1

2

那是因为auto&不适用于结构化绑定。它应用于引用该结构的基础实体。在您的 cppinsights 片段中,这将是__f17.

如果您要auto [x]改用,代码段将扩展为类似这样的内容

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否则,如果qualified-idstd​::​tuple_­size<E>命名了一个完整的类型,则表达式std​::​tuple_­size<E>​::​value应为格式良好的整数常量表达式,并且标识符列表中的元素数应等于该表达式的值。unqualified-id在按类成员访问查找get的范围内E查找,如果找到至少一个声明,则初始化程序为e.get<i>(). 否则,初始值设定项是get<i>(e),其中 get 在关联的命名空间中查找。在任何一种情况下,get<i>都被解释为模板 ID。[注:不执行普通的非限定查找。— 结束注释] 在任何一种情况下,e如果实体的类型e是左值引用,则为左值,否则为 xvalue。给定类型Ti由 指定 std​::​tuple_­element<i, E>​::​type,每个vi都是使用初始化程序初始化的“reference to”类型的变量,Ti如果初始化程序是左值,则引用是左值引用,否则是右值引用;引用的类型是Ti.

于 2020-04-21T10:25:36.787 回答