C ++十进制类型和括号-为什么?


32

之前已经讨论 该主题,但这不是重复的。

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

首先,a也是一个表达。主表达式的选项包括:

  • (表情)
  • id表达

更重要的是,decltype的措词非常非常明确地考虑了括号

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, ...

因此问题仍然存在。为什么括号要区别对待?是否有人熟悉其背后的技术论文或委员会讨论?括号的明确考虑导致认为这不是疏忽,所以一定有我所缺少的技术原因。


2
“通常的答案是-a是变量,(a)是表达式”,它们的意思是“ (a)是表达式,并且a是表达式变量”。
HolyBlackCat

Answers:


18

这不是疏忽。有趣的是,在Decltype和auto(修订版4)(N1705 = 04-0145)中有一条语句:

现在,decltype规则明确声明了这一点decltype((e)) == decltype(e)(如EWG所建议)。

但是在Decltype(修订版6)中:建议的措词(N2115 = 06-018)的变化之一是

在decltype内部的括号表达式不被视为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号文件明确地增加了这个有趣的小括号的行为,但跳过的理由:(我想这是接近的答案,因为我们希望现在得到..
地平线Shilon

但是,没有人提出通过为两个不同的功能使其成为两个不同的关键字来解决这个愚蠢的问题吗?委员会最大的错误之一(历史上犯了很多非强制性的错误)。
curiousguy

13

为什么括号要区别对待?

括号没有区别。区别对待的是非括号化的id表达式。

如果出现括号,则适用所有表达式的规则。提取类型和值类别并将其分类为decltype

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

如果我们确实将变量的属性视为表达式,那么仍然可以很容易地获得它,并带有额外的一对括号。


因此,对于一个int成员iadecltype(a.i)int同时decltype((a.i))int&(假设a没有const)?既然表达式a.i是可分配的?
n314159

1
@ n314159-这就是要点。该表达式a.i是非常量左值,因此您获得的非常量左值引用类型(a.i)
StoryTeller-Unslander Monica

为什么“所有表达式的规则”表明它们的类型是引用?为什么“变量作为表达式的属性”包含作为引用的属性?这是自然的默认值吗?
Ofek Shilon

1
@OfekShilon-它们的类型不是引用。表达式的类型永远不会作为参考进行分析。但是dectlype只能解析为类型,这不仅要告诉我们表达式的类型,还要告诉我们其值的类别。值类别编纂通过引用类型。lvalues是&,xvalues是&&,prvalues不是引用类型。
StoryTeller-Unslander Monica

@StoryTeller完全一样,表达式类型和非表达式类型之间没有“自然”差异。这使得区分是人为的。
Ofek Shilon

1

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

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

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

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

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


我记得在cpp谈话中听到过此消息。但是,我没有找到来源的希望。如果有人找到它,那就太好了。
bolov

1
C ++历史上添加了一堆新关键字,许多没有下划线,有些只有一个英语单词。理性确实是荒谬的。
curiousguy

@curiousguy引入的每个关键字都将与现有的用户符号冲突,因此委员会在决定向该语言添加新的保留关键字方面投入了很大的精力。这不是荒谬的imo。
bolov

不正确:export已引入。如果可以的话export(以前默认情况下所有模板都是“导出”的),则可以有decltype和的东西constexpr。显然,添加register另一种语言将是有问题的。
curiousguy

@bolov谢谢!(1)是推测还是知识?您是否知道在动机中避免使用额外关键字的讨论?(2)您能否举一个例子说明一个id表达式如果被“用作表达式”时实际上需要区别对待吗?(这甚至意味着什么?)
Ofek Shilon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.