1. 歧義消除
C++中類似int(A)
的代碼會引起語法歧義,其可以解釋為:
- 對象聲明磷醋,即兩邊具有冗余圓括號的對象聲明余蟹,其等同于
int A
- 表達式語句,即函數(shù)式類型轉(zhuǎn)換燕锥,其等同于
(int)A
- 函數(shù)類型聲明辜贵,即
A
是類型名時,其等同于int (*fp)(A a)
對于表達式語句與對象聲明的二義性归形,C++標(biāo)準(zhǔn) 8.9 Ambiguity resolution 中提到
1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
2 [Note: If the statement cannot syntactically be a declaration, there is no ambiguity, so this rule does not apply. The whole statement might need to be examined to determine whether this is the case. This resolves the meaning of many examples. [Example: Assuming T is a simple-type-specifier,
解釋:
(1) 在涉及表達式語句和聲明的語法中存在歧義托慨,則將函數(shù)式型轉(zhuǎn)解析為其最左邊的子表達式的一個聲明
(2) 若語法上不能是聲明,則不存在歧義暇榴,解析為函數(shù)式類型轉(zhuǎn)換
標(biāo)準(zhǔn)中的例子:
// Example: Assuming T is a simple-type-specifier
T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a, 5) << c; // expression-statement
T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
9.3.2 Ambiguity resolution 中還提到
1 The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 8.9 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 8.9, the resolution is to consider any construct that could possibly be a declaration a declaration.
解釋:
此歧義還會出現(xiàn)在聲明的上下文中厚棵,例如函數(shù)類型的聲明。在這種情況下蔼紧,可在參數(shù)名稱周圍帶有一組冗余括號的函數(shù)聲明和以函數(shù)樣式強制轉(zhuǎn)換作為初始值設(shè)定項的對象聲明之間進行選擇婆硬。解決方案是將任何可能是聲明的構(gòu)造視為聲明。
即將int(A)
優(yōu)先考慮為對象聲明奸例,從而整體解析為參數(shù)名稱周圍帶有一組冗余括號的函數(shù)聲明彬犯。
標(biāo)準(zhǔn)中的例子:
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 y((int)a); // object declaration
S z = int(a); // object declaration
}
2 An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
解釋:
函數(shù)式類型轉(zhuǎn)換和類型標(biāo)識之間歧義。解決方案是,在其語法上下文中可能是類型標(biāo)識的構(gòu)造都應(yīng)被視為類型標(biāo)識躏嚎。
即語法上優(yōu)先視為類型標(biāo)識,若不合法菩貌,則視為函數(shù)式類型轉(zhuǎn)換表達式
標(biāo)準(zhǔn)中的例子:
template <class T> struct X {};
template <int N> struct Y {};
X<int()> a; // type-id
X<int(1)> b; // expression (ill-formed)
Y<int()> c; // type-id (ill-formed)
Y<int(1)> d; // expression
void foo(signed char a) {
sizeof(int()); // type-id (ill-formed)
sizeof(int(a)); // expression
sizeof(int(unsigned(a))); // type-id (ill-formed)
(int()) + 1; // type-id (ill-formed)
(int(a)) + 1; // expression
(int(unsigned(a))) + 1; // type-id (ill-formed)
}
3 Another ambiguity arises in a parameter-declaration-clause when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id.
解釋:
當(dāng)類型名嵌套在圓括號中卢佣,參數(shù)聲明子句中會出現(xiàn)二義性。在這種情況下箭阶,可在聲明函數(shù)指針類型的參數(shù)和對象聲明周圍帶有冗余括號的參數(shù)之間進行選擇虚茶。解決方法是將類型名稱視為簡單類型說明符。
即將int(A)
稱視為函數(shù)類型聲明仇参,其等同于int (*fp)(A a)
標(biāo)準(zhǔn)中的例子:
class C { };
void f(int(C)) { } // void f(int(*fp)(C c)) { }
// not: void f(int C) { }
int g(C);
void foo() {
f(1); // error: cannot convert 1 to function pointer
f(g); // OK
}
注:上文提及的C++標(biāo)準(zhǔn)為 C++ 20 ISO/IEC 14882:2020 草案版本 N4849
2. 迭代器的operator++
在對關(guān)聯(lián)類容器遍歷進行刪除時嘹叫,會這樣做:
for (auto iter = m_map.begin(); iter != m_map.end();)
{
if (刪除條件) {
m_map.erase(iter++);
} else {
++iter;
}
}
在對關(guān)聯(lián)類容器使用erase()
去刪除時,會使當(dāng)前迭代器失效诈乒,那上文中的m_map.erase(iter++);
在刪除之后罩扇,iter
不就失效了嗎?為什么還能iter++
怕磨,指向容器中的后一個元素喂饥?
看下迭代器對++操作的重載實現(xiàn):
// 以vector 迭代器為例,Windows版本如下
_Vector_const_iterator operator++(int) noexcept {
_Vector_const_iterator _Tmp = *this;
++* this;
return _Tmp;
}
可以看到實現(xiàn)中肠鲫,用局部對象_Tmp
指向了當(dāng)前位置员帮,然后對迭代器進行了++操作,最后返回了局部對象_Tmp
导饲。
所以捞高,iter++
實際上已經(jīng)先對迭代器進行了++操作(此時,迭代器還未失效)渣锦,但最終返回了++前的值提供給erase()
去刪除硝岗。
從源碼也可以看出,后置++需要多生成一個局部對象_Tmp
袋毙,因此效率不及前置++辈讶。這也是為什么在循環(huán)中,一般對迭代器使用前置++的原因娄猫。