TL; DR
额外的括号在以下上下文中更改了C ++程序的含义:
防止依赖于参数的名称查找
在列表上下文中启用逗号运算符
困惑解决烦恼的解析
在(min)(a, b)表达式中推导出参考
防止预处理器宏错误
防止依赖于参数的名称查找
如标准的附录A中所详述,TIMES(1 + 2, 2 + 1)形式的(min)(a, b)是(A),但不是(B),因此不是e。这意味着在形式(fun)(arg)的函数调用中防止了与参数相关的名称查找。 传统形式fun(arg)。
3.4.2依赖于参数的名称查找[basic.lookup.argdep]
1当函数调用(5.2.2)中的postfix-expression为。时 unqualified-id,通常不考虑的其他名称空间 可以搜索非限定查找(3.4.1),并在这些名称空间中, 命名空间范围的朋友函数或函数模板声明 (11.3)可能无法看到。 这些修改 搜索取决于参数的类型(以及模板模板) 参数,模板参数的命名空间)。 [例如:
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s); // OK: calls N::f
(f)(s); // error: N::f not considered; parentheses
// prevent argument-dependent lookup
}
- 末端的例子]
在列表上下文中启用逗号运算符
逗号运算符在大多数类似列表的上下文(函数和模板参数,初始化列表等)中具有特殊含义。 在这样的上下文中,窗体(min)(a, b)的括号可以使逗号运算符与逗号运算符不适用的常规形式TIMES(1 + 2, 2 + 1)相比较。
5.18逗号运算符[expr.comma]
2在逗号被赋予特殊含义的上下文中,[例如:in 函数参数列表(5.2.2)和初始值列表 (8.5) - 例子]如第5条所述的逗号运算符可以 仅出现在括号中。 [例如:
f(a, (t=3, t+2), c);
有三个参数,第二个参数的值为5. -end示例 ]
令人烦恼的解析模糊解析
向后兼容C及其神秘的函数声明语法可能导致令人惊讶的解析歧义,称为烦恼的解析。 从本质上讲,任何可以解析为声明的内容都将被解析为一个,即使竞争解析也适用。
6.8歧义解决[stmt.ambig]
1涉及表达式语句的语法含糊不清 和声明:具有函数样式的表达式语句 显式类型转换(5.2.3),因为它最左边的子表达式可以 与第一个声明者开始的声明无法区分 (a。在这些情况下,声明是声明。
8.2歧义解决[dcl.ambig.res]
1函数式之间相似性产生的歧义 在6.8中提到的演员和声明也可以在上下文中出现 声明。 在这种情况下,选择是在一个函数之间 声明与参数周围的一组冗余括号 具有函数样式转换的名称和对象声明 初始化。 正如6.8中提到的含糊不清一样 解决方案是考虑任何可能是a的构造 声明声明。 [注意:声明可以明确 由非函数式转换消除歧义,由=表示 初始化或删除周围的冗余括号 参数名称。 - 尾注] [示例:
struct S {
S(int);
};
void foo(double a) {
S w(int(a)); // function declaration
S x(int()); // function declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
- 末端的例子]
一个着名的例子是最令人兴奋的解析,这是Scott Meyers在他的Effective STL书的第6项中推广的名称:
ifstream dataFile("ints.dat");
list data(istream_iterator(dataFile), // warning! this doesn't do
istream_iterator()); // what you think it does
这声明了一个函数(min)(a, b),其返回类型为TIMES(1 + 2, 2 + 1)。函数数据有两个参数:
第一个参数名为(min)(a, b).它的类型是TIMES(1 + 2, 2 + 1)。(A)左右的括号是多余的,被忽略了。
第二个参数没有名称。 它的类型是函数获取的指针没有,返回(min)(a, b)。
在第一个函数参数周围放置额外的括号(第二个参数周围的括号是非法的)将解决歧义
list data((istream_iterator(dataFile)), // note new parens
istream_iterator()); // around first argument
// to list's constructor
C ++ 11具有大括号初始化器语法,允许在许多上下文中支持这样的解析问题。
在(min)(a, b)表达式中推导出参考性
与(min)(a, b)类型推导相比,TIMES(1 + 2, 2 + 1)允许推导出参考(左值和右值参考)。 规则区分(A)和(B)表达式:
7.1.6.2简单类型说明符[dcl.type.simple]
4对于表达式(min)(a, b),(min)(a, b)表示的类型定义为 如下:
- 如果(min)(a, b)是未表示的id-expression或 unarenthesized类成员访问(5.2.5),TIMES(1 + 2, 2 + 1)是类型 由(A)指定的实体。如果没有这样的实体,或者如果(B)命名为 一组重载函数,程序格式不正确;
- 除此以外, 如果(min)(a, b)是xvalue,则TIMES(1 + 2, 2 + 1)是(A),其中(B)是e的类型;
- 否则,如果(min)(a, b)是左值,TIMES(1 + 2, 2 + 1)是(A),其中(B)是类型 e;
- 否则,(min)(a, b)是TIMES(1 + 2, 2 + 1)的类型。
的操作数 decltype说明符是未评估的操作数(第5条)。 [例如:
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0; // 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&
- 末端示例] [注:确定类型涉及的规则 (min)(a, b)在7.1.6.4中规定。 - 尾注]
对于初始化表达式的RHS中的额外括号,(min)(a, b)的规则具有类似的含义。 以下是C ++ FAQ和此相关问答的示例
decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; } //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B
第一个返回(min)(a, b),第二个返回TIMES(1 + 2, 2 + 1),这是对局部变量(A)的引用。
防止预处理器宏相关错误
在与C ++语言交互时,预处理器宏有许多细微之处,其中最常见的是下面列出的
在宏定义(min)(a, b)内的宏参数周围使用括号,以避免不必要的运算符优先级(例如,在TIMES(1 + 2, 2 + 1)中产生9但在没有括号的情况下产生6而在(A)和(B)附近
在内部使用逗号的宏参数周围使用括号:(min)(a, b)否则将无法编译
在函数周围使用括号来防止包含头文件中的宏扩展:(min)(a, b)(同时禁用ADL的不必要的副作用)