【问题标题】:C++ decltype and parentheses - why?C++ decltype 和括号 - 为什么?
【发布时间】:2020-06-08 01:19:53
【问题描述】:

主题是discussedbefore,但这不是重复的。

当有人问到decltype(a)decltype((a)) 的区别时,通常的答案是——a 是一个变量,(a) 是一个表达式。我觉得这个答案不令人满意。

首先,a 也是一个表达式。 primary expression 的选项包括 -

  • (表达式)
  • id 表达式

更重要的是,decltype considers parentheses very, very explicitly 的措辞:

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

所以问题仍然存在。 为什么括号的处理方式不同?是否有人熟悉其背后的技术论文或委员会讨论?对括号的明确考虑导致认为这不是疏忽,所以一定有我遗漏的技术原因。

【问题讨论】:

  • “通常的答案是——a 是一个变量,(a) 是一个表达式” 他们的意思是“(a) 是一个表达式,而a 是一个表达式一个变量”。

标签: c++ c++11 language-lawyer decltype


【解决方案1】:

这不是疏忽。有趣的是,在Decltype and auto (revision 4) (N1705=04-0145)中有这样一句话:

decltype 规则现在明确声明 decltype((e)) == decltype(e)(如 EWG 所建议)。

但在 Decltype (revision 6):建议的措辞 (N2115=06-018) 中,其中一项更改是

decltype 内的Parenthesized-expression 不被视为id-expression

措辞没有任何理由,但我认为这是对 decltype 的一种扩展,使用了一些不同的语法,换句话说,它旨在区分这些情况。

C++draft9.2.8.4 中显示了它的用法:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

真正有趣的是它如何与return 语句一起工作:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

我的 Visual Studio 2019 建议我删除多余的括号,但实际上它们变成了 decltype((i)),这会将返回值更改为 int&,这使它成为 UB,因为返回对局部变量的引用。

【讨论】:

  • 感谢指点出处!不仅可以以不同的方式指定 decltype,而且最初是这样的。 rev-6 文档明确添加了这种有趣的括号行为,但跳过了基本原理:(。我想这与我们现在得到的答案一样接近..
  • 然而没有人提出通过将两个不同的关键字设置为两个不同的功能来解决这个愚蠢的问题?委员会最大的错误之一(历史上犯了很多非受迫性错误)。
【解决方案2】:

为什么括号的处理方式不同?

括号没有区别对待。区别对待的是未加括号的 id 表达式。

当括号出现时,all 表达式的常规规则适用。类型和值类别被提取并编码为decltype的类型。

那里有特殊规定,以便我们可以更轻松地编写有用的代码。在将decltype 应用于(成员)变量的名称时,我们通常不希望某些类型在被视为表达式时表示该变量的属性。相反,我们只需要声明变量的类型,而无需应用大量类型特征来获取它。这正是decltype 指定给我们的内容。

如果我们确实关心作为表达式的变量的属性,那么我们仍然可以很容易地得到它,只需要一对额外的括号。

【讨论】:

  • 所以对于aint成员idecltype(a.i)int,而decltype((a.i))int&(假设a不是@98765433333)既然表达式a.i 是可赋值的?
  • @n314159 - 这就是它的要点。表达式a.i 是一个非常量左值,因此您将获得(a.i) 的非常量左值引用类型。
  • 为什么“所有表达式的正则规则”表明它们的类型是引用?为什么“作为表达式的变量的属性”包括作为引用的属性?这是一些自然的默认值吗?
  • @OfekShilon - 他们的类型不是参考。 The type of an expression is never analyzed as a reference。但是 declype 只能解析为一个类型,它不仅要告诉我们表达式的类型,还要告诉我们它的值类别。值类别由引用类型编码。 lvalues 是&,xvalues 是&&,prvalues 不是引用类型。
  • @StoryTeller 确切地说,表达式类型和非表达式之间没有“自然”的区别。这使得区分是人为的。
【解决方案3】:

C++11 之前的语言需要工具来获取两种不同的信息

  • 表达式的类型
  • 声明变量时的类型

由于这些信息的性质,必须在语言中添加功能(不能在库中完成)。这意味着新的关键字。该标准可能为此引入了两个新关键字。例如exprtype 获取表达式的类型,decltype 获取变量的声明类型。那将是一个明确而快乐的选择。

然而,标准委员会一直尽最大努力避免在语言中引入新的关键字,以尽量减少对旧代码的破坏。向后兼容是该语言的核心理念。

所以在 C++11 中,我们只有一个关键字用于两种不同的事情:decltype。它区分这两种用途的方式是区别对待decltype(id-expression)。这是委员会有意识的决定,是一个(小的)妥协。

【讨论】:

  • 我记得在一个 cpp 演讲中听到这个。然而,我没有希望找到源头。如果有人找到它会很棒。
  • C++ 历史上添加了一堆新的关键字,许多没有下划线,有些只有一个英文单词。理性真的很荒谬。
  • @curiousguy 引入的每个关键字都会与现有的用户符号发生冲突,因此委员会在决定向该语言中添加新的保留关键字时非常重视。这并不荒谬。
  • @curiousguy bolov 并不是说​​没有添加关键字,而是委员会更愿意不添加关键字,正是因为每个新关键字都可能是一个重大变化。
  • @OfekShilon auto 曾经是自动存储持续时间变量的(可选)说明符,以区别于staticregister
猜你喜欢
  • 2011-03-07
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多