结构化绑定中的 cv 限定符传播

Nim*_*rod 7 c++ qualifiers language-lawyer c++17 structured-bindings

正如dcl.struct.bind中引用的那样,

让 cv 表示 decl-specifier-seq 中的 cv 限定符。

将 E 的非静态数据成员指定为 m 0 、 m 1 、 m 2 、...(按声明顺序),每个 vi 是一个左值的名称,该左值引用 e 的成员 mi ,其类型为 cv T i ,其中 T i 是该成员的声明类型;

如果我理解正确,cv 限定符是从结构化绑定的声明中传播的。

假设我有一个简单的结构,

struct Foo {
    int x;
    double y;
};

Run Code Online (Sandbox Code Playgroud)

考虑两种情况,

const Foo f{1, 1.0};
auto& [x, y] = f;
// static_assert(std::is_same<decltype(x), int>::value); // Fails!
static_assert(std::is_same<decltype(x), const int>::value); // Succeeds
Run Code Online (Sandbox Code Playgroud)

现场演示。的 cv-qualifier 是否x来自扣除auto

第二个,

Foo f{1, 1.0};

const auto& [x, y] = f;
const auto& rf = f;

static_assert(std::is_same<decltype(x), const int>::value); // with const
static_assert(std::is_same<decltype(rf.x), int>::value); // without const
Run Code Online (Sandbox Code Playgroud)

现场演示。结果符合标准,有道理。

我的第二个问题是是否有任何理由传播 cv 限定符,这不是一种不一致(对初始化的引用auto)吗?

use*_*522 3

decltype当类的成员直接命名为不带括号的成员访问表达式时,有一个特殊的规则。如果表达式被视为表达式,则通常不会生成结果,而是生成成员的声明类型。

所以decltype(rf.x)给出int,因为x被声明为int。您可以decltype通过添加额外的括号 () 来强制执行与其他表达式相同的行为decltype((rf.x)),在这种情况下它将给出,const int&因为它是左值表达式并且是通过const引用进行访问。

decltype同样,如果直接命名结构化绑定(不带括号),也有特殊规则,这就是为什么您不会获得const int&for decltype(x)

但是,如果成员不是引用类型,则结构化绑定的规则会将成员访问表达式中的类型作为表达式const,这就是传播的原因。至少自从CWG 问题 2312的后 C++20 决议以来就是这样,该决议旨在使const传播与mutable成员正确地进行。

在解析之前,结构化绑定的类型实际上被指定为成员的声明类型,并添加了结构化绑定声明的 cv 限定符,正如您在问题中引用的那样。

我可能会遗漏一些关于声明的类型到底指的是什么的细节,但在我看来,这实际上并没有指定在第一个片段中x具有类型(因此也不是),尽管这似乎是所有编译器总是处理的方式这种情况也是唯一有意义的行为。也许这是另一个缺陷,被 CWG 2312 默默地或无意地修复了。const int&decltypeconst

因此,实际上,当您将它们用作表达式时,在您的示例中, 和 都是左值rf.x表达式xconst int这里唯一奇怪的是decltype行为方式。