![](/img/trans.png)
[英]Why does postfix operator++ have higher precedence than prefix operator++?
[英]When do extra parentheses have an effect, other than on operator precedence?
C ++中的括號用於許多地方:例如,在函數調用和分組表達式中,以覆蓋運算符優先級。 除了非法的額外括號 (例如函數調用參數列表周圍)之外,C ++的一般但非絕對規則是額外的括號永遠不會受到傷害 :
5.1主要表達式[expr.prim]
5.1.1一般[expr.prim.general]
6帶括號的表達式是一個主表達式,其類型和值與所附表達式的類型和值相同。 括號的存在不會影響表達式是否為左值。 除非另有說明,否則帶括號的表達式可以在與可以使用所包含的表達式的上下文完全相同的上下文中使用,並且具有相同的含義。
問題 :在哪些上下文中,額外的括號會改變C ++程序的含義,而不是覆蓋基本的運算符優先級?
注意 :我認為指針到成員語法的限制沒有括號的&qualified-id
超出范圍,因為它限制語法而不是允許兩個具有不同含義的語法。 同樣, 在預處理程序宏定義中使用括號也可以防止不必要的運算符優先級。
額外的括號在以下上下文中更改了C ++程序的含義:
decltype
表達式中推導出參考性 如在標准中,附錄A中詳細post-fix expression
的形式的(expression)
是一個primary expression
,但不是一個id-expression
,並且因此不是unqualified-id
。 這意味着與傳統的形式fun(arg)
相比,在形式(fun)(arg)
函數調用中阻止了與參數相關的名稱查找。
3.4.2依賴於參數的名稱查找[basic.lookup.argdep]
1當函數調用(5.2.2)中的postfix-expression是非限定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
}
- 末端的例子]
逗號運算符在大多數類似列表的上下文(函數和模板參數,初始化列表等)中具有特殊含義。 在這樣的上下文中,形式a, (b, c), d
括號可以使逗號運算符與逗號運算符不適用的常規形式a, b, c, d
相比較。
5.18逗號運算符[expr.comma]
2在逗號被賦予特殊含義的上下文中,[示例: 在函數的參數列表(5.2.2)和初始化器列表(8.5) -示例中]如第5章所述的逗號運算符只能出現在括號中。 [例如:
f(a, (t=3, t+2), c);
有三個參數,第二個參數的值為5. -end example]
向后兼容C及其神秘的函數聲明語法可能導致令人驚訝的解析歧義,稱為煩惱的解析。 從本質上講, 任何可以解析為聲明的內容都將被解析為一個 ,即使競爭解析也適用。
6.8歧義解決[stmt.ambig]
1 語法中涉及表達式語句和聲明存在歧義 :具有函數式顯式類型轉換(5.2.3)的表達式語句,因為其最左側的子表達式與第一個聲明符以a開頭的聲明無法區分( 在這些情況下,該聲明是一份聲明 。
8.2歧義解決[dcl.ambig.res]
1 由於函數式轉換與6.8中提到的聲明之間的相似性而產生的歧義也可以在聲明的上下文中出現 。 在該上下文中,選擇在函數聲明與參數名稱周圍的冗余括號集和具有函數樣式轉換作為初始化器的對象聲明之間。 正如6.8中提到的含糊不清一樣,該決議是考慮任何可能是聲明聲明的結構 。 [注意:聲明可以通過非函數式轉換顯式消除歧義,通過=表示初始化或刪除參數名稱周圍的冗余括號。 - 尾注] [示例:
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<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
istream_iterator<int>()); // what you think it does
這聲明了一個函數data
,其返回類型是list<int>
。 功能數據有兩個參數:
dataFile
。 它的類型是istream_iterator<int>
。 dataFile
周圍的括號是多余的,被忽略。 istream_iterator<int>
。 在第一個函數參數周圍放置額外的括號(第二個參數周圍的括號是非法的)將解決歧義
list<int> data((istream_iterator<int>(dataFile)), // note new parens
istream_iterator<int>()); // around first argument
// to list's constructor
C ++ 11具有大括號初始化器語法,允許在許多上下文中支持這種解析問題。
decltype
表達式中推導出參考性 與auto
類型推導相反, decltype
允許推導出參考(左值和右值引用)。 規則區分decltype(e)
和decltype((e))
表達式:
7.1.6.2簡單類型說明符[dcl.type.simple]
4對於表達式
e
, 由decltype(e)
表示的類型定義如下:- 如果
e
是未表示的id-expression或未加密的類成員訪問(5.2.5),則decltype(e)
是e
命名的實體的類型。 如果沒有這樣的實體,或者如果e
命名了一組重載函數,那么該程序就會形成錯誤;- 否則,如果
e
是x值,則decltype(e)
是T&&
,其中T
是e
的類型;- 否則,如果
e
是左值,則decltype(e)
是T&
,其中T
是e
的類型;-否則,
decltype(e)
是的類型e
。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&
-end example] [注意:確定涉及
decltype(auto)
類型的規則在7.1.6.4中規定。 - 尾注]
decltype(auto)
的規則對於初始化表達式的RHS中的額外括號具有類似的含義。 以下是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
第一個返回string
,第二個返回string &
,它是對局部變量str
的引用。
在與C ++語言交互時,預處理器宏有許多細微之處,其中最常見的是下面列出的
#define TIMES(A, B) (A) * (B);
為了避免不必要的運算符優先級(例如,在TIMES(1 + 2, 2 + 1)
中產生9但在6 (A)
和(B)
周圍沒有括號的情況下產生6 assert((std::is_same<int, int>::value));
否則將無法編譯 (min)(a, b)
(同時禁用ADL的副作用) 通常,在編程語言中,“額外”括號意味着它們不會改變語法分析順序或含義。 為了使人們閱讀代碼的好處,它們被添加以澄清順序(運算符優先級),它們的唯一作用是稍微減慢編譯過程,並減少理解代碼時的人為錯誤(可能加速整個開發過程) )。
如果一組括號實際上改變了表達式的解析方式,那么根據定義它們不是額外的。 將非法/無效解析變成合法解析的括號不是“額外的”,盡管這可能指出了一種糟糕的語言設計。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.