C++decltype和圆括号-为什么
C++ decltype and parentheses - why?
前面讨论过这个主题,但这不是重复的。
当有人问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, ...
因此,问题依然存在为什么括号被区别对待?有人熟悉技术文件或其背后的委员会讨论吗?对括号的明确考虑导致人们认为这不是疏忽,所以我遗漏了一定有技术原因。
这不是疏忽。有趣的是,在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
的类型中。
那里有特殊条款,这样我们就可以更容易地编写有用的代码。当将decltype
应用于(成员(变量的名称时,我们通常不希望使用某种类型来表示作为表达式处理的变量的属性。相反,我们只需要声明变量的类型,而不必应用大量的类型特征。这正是decltype
指定给我们的。
如果我们确实关心变量作为表达式的属性,那么我们仍然可以很容易地获得它,只需多加一对括号。
在C++11之前,语言需要工具来获得两种不同类型的信息:
- 表达式的类型
- 变量声明时的类型
由于这些信息的性质,必须在语言中添加这些功能(这不能在库中完成(。这意味着新的关键字。该标准本可以为此引入两个新的关键字。例如,exprtype
获取表达式的类型,decltype
获取变量的声明类型。这将是一个明确而愉快的选择。
然而,标准委员会总是尽最大努力避免在语言中引入新的关键字,以最大限度地减少旧代码的破坏。向后兼容性是语言的核心哲学。
因此,在C++11中,我们只得到了一个用于两种不同事物的关键字:decltype
。区分这两种用途的方法是对decltype(id-expression)
进行不同的处理。这是委员会有意识的决定,是一个(小(妥协。
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 为什么在全局范围内使用"extern int a"似乎不行?
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 为什么会发生堆损坏
- 为什么使用 "this" 指针调用派生成员函数?
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 为什么比较运算符如此快速
- 为什么 Serial.println(<char[]>);返回随机字符?
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 为什么不;名字在地图上是按顺序排列的吗
- 我的字符计数代码计算错误.为什么
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 为什么野牛仍在使用"int yylex(void)",却找不到"int yylex(YYS
- C++decltype和圆括号-为什么
- 为什么标准首选圆括号初始化为"make_<something>"?
- 为什么圆括号在初始化空向量时起作用?