TL; ดร
วงเล็บเสริมจะเปลี่ยนความหมายของโปรแกรม C ++ ในบริบทต่อไปนี้:
- ป้องกันการค้นหาชื่อที่ขึ้นกับอาร์กิวเมนต์
- การเปิดใช้ตัวดำเนินการลูกน้ำในบริบทรายการ
- การแก้ปัญหาความคลุมเครือของการแยกวิเคราะห์ที่รบกวน
- การอนุมานการอ้างอิงใน
decltype
นิพจน์
- ป้องกันข้อผิดพลาดมาโครตัวประมวลผลล่วงหน้า
การป้องกันการค้นหาชื่อที่ขึ้นกับอาร์กิวเมนต์
ในฐานะที่เป็นรายละเอียดในภาคผนวกของมาตรฐานเป็นpost-fix expression
ของแบบฟอร์ม(expression)
เป็นprimary expression
แต่ไม่ได้และดังนั้นจึงไม่ได้เป็นid-expression
unqualified-id
ซึ่งหมายความว่าอาร์กิวเมนต์ขึ้นอยู่กับการค้นหาชื่อถูกขัดขวางในการเรียกฟังก์ชั่นในรูปแบบเมื่อเทียบกับรูปแบบเดิม(fun)(arg)
fun(arg)
3.4.2 การค้นหาชื่อตามอาร์กิวเมนต์ [basic.lookup.argdep]
1 เมื่อนิพจน์ postfix ในการเรียกใช้ฟังก์ชัน (5.2.2) เป็นรหัสที่ไม่ถูกต้องอาจมีการค้นหาเนมสเปซอื่นที่ไม่ได้รับการพิจารณาในระหว่างการค้นหาที่ไม่มีเงื่อนไขตามปกติ (3.4.1) และในเนมสเปซเหล่านั้นฟังก์ชันเนมสเปซขอบเขตเพื่อนหรือ การประกาศเทมเพลตฟังก์ชัน (11.3) อาจมองไม่เห็นเป็นอย่างอื่น การปรับเปลี่ยนการค้นหาเหล่านี้ขึ้นอยู่กับประเภทของอาร์กิวเมนต์ (และสำหรับอาร์กิวเมนต์เทมเพลตเทมเพลตเนมสเปซของอาร์กิวเมนต์เทมเพลต) [ตัวอย่าง:
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s);
(f)(s);
}
- ส่งตัวอย่าง]
การเปิดใช้งานตัวดำเนินการลูกน้ำในบริบทรายการ
ตัวดำเนินการลูกน้ำมีความหมายพิเศษในบริบทที่เหมือนรายการส่วนใหญ่ (อาร์กิวเมนต์ของฟังก์ชันและเทมเพลตรายการตัวเริ่มต้น ฯลฯ ) วงเล็บของแบบฟอร์ม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 - ท้ายตัวอย่าง]
การแก้ปัญหาความไม่ชัดเจนของการแยกวิเคราะห์ที่รบกวน
ความเข้ากันได้แบบย้อนหลังกับ C และไวยากรณ์การประกาศฟังก์ชัน arcane อาจทำให้เกิดความคลุมเครือในการแยกวิเคราะห์ที่น่าแปลกใจซึ่งเรียกว่าการแยกวิเคราะห์ที่ทำให้รำคาญ โดยพื้นฐานแล้วสิ่งที่สามารถแยกวิเคราะห์เป็นคำประกาศจะถูกแยกวิเคราะห์เป็นรายการเดียวแม้ว่าจะมีการใช้การแยกวิเคราะห์ที่แข่งขันกันก็ตาม
6.8 การแก้ไขความคลุมเครือ [stmt.ambig]
1 มีความคลุมเครือในไวยากรณ์ที่เกี่ยวข้องกับนิพจน์ - คำสั่งและการประกาศ : นิพจน์ - คำสั่งที่มีการแปลงประเภท Explicit สไตล์ฟังก์ชัน (5.2.3) เนื่องจากนิพจน์ย่อยด้านซ้ายสุดสามารถแยกไม่ออกจากการประกาศโดยที่ตัวประกาศแรกเริ่มต้นด้วย ( . ในกรณีดังกล่าวเป็นคำสั่งประกาศ
8.2 การแก้ไขความคลุมเครือ [dcl.ambig.res]
1 ความคลุมเครือที่เกิดขึ้นจากความคล้ายคลึงกันระหว่างนักแสดงฟังก์ชั่นสไตล์และการประกาศที่กล่าวถึงใน 6.8 ยังสามารถเกิดขึ้นในบริบทของการประกาศที่ ในบริบทดังกล่าวทางเลือกอยู่ระหว่างการประกาศฟังก์ชันกับชุดวงเล็บที่ซ้ำซ้อนรอบชื่อพารามิเตอร์และการประกาศอ็อบเจ็กต์ที่มีการแคสต์ลักษณะฟังก์ชันเป็นตัวเริ่มต้น เช่นเดียวกับที่สำหรับงงงวยที่กล่าวถึงใน 6.8 ความละเอียดคือการพิจารณาสร้างใด ๆ ที่อาจจะประกาศประกาศ [หมายเหตุ: การประกาศสามารถแยกออกได้อย่างชัดเจนโดยการแคสต์แบบไม่ใช้งานฟังก์ชันโดย = เพื่อระบุการเริ่มต้นหรือโดยการลบวงเล็บที่ซ้ำซ้อนรอบชื่อพารามิเตอร์ —end note] [ตัวอย่าง:
struct S {
S(int);
};
void foo(double a) {
S w(int(a));
S x(int());
S y((int)a);
S z = int(a);
}
- ส่งตัวอย่าง]
ตัวอย่างที่มีชื่อเสียงของเรื่องนี้คือMost Vexing Parseซึ่งเป็นชื่อที่นิยมโดย Scott Meyers ในรายการที่ 6 ของหนังสือEffective STLของเขา:
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
istream_iterator<int>());
นี้ประกาศฟังก์ชั่นที่มีผลตอบแทนประเภทคือdata
list<int>
ข้อมูลฟังก์ชันใช้สองพารามิเตอร์:
dataFile
พารามิเตอร์ตัวแรกเป็นชื่อ ประเภทคือistream_iterator<int>
. วงเล็บรอบdataFile
เป็นสิ่งที่ไม่จำเป็นและถูกละเว้น
- พารามิเตอร์ที่สองไม่มีชื่อ ประเภทของมันคือตัวชี้ให้ทำงานโดยไม่ต้องใช้อะไรเลยและส่งคืน
istream_iterator<int>
ไฟล์.
การวางวงเล็บเสริมรอบอาร์กิวเมนต์แรกของฟังก์ชัน (วงเล็บรอบอาร์กิวเมนต์ที่สองไม่ถูกต้อง) จะแก้ไขความคลุมเครือ
list<int> data((istream_iterator<int>(dataFile)),
istream_iterator<int>());
C ++ 11 มีไวยากรณ์ brace-initializer ที่ช่วยให้สามารถแบ่งขั้นตอนปัญหาการแยกวิเคราะห์ดังกล่าวในหลาย ๆ บริบท
การหักล้างการอ้างอิงในdecltype
นิพจน์
ตรงกันข้ามกับauto
การหักประเภทdecltype
อนุญาตให้มีการอนุมานการอ้างอิง (การอ้างอิง lvalue และ rvalue) กฎแยกความแตกต่างระหว่างdecltype(e)
และdecltype((e))
นิพจน์:
7.1.6.2 ตัวระบุประเภทอย่างง่าย [dcl.type.simple]
4 เพราะการแสดงออกe
, ประเภทแสดงโดยdecltype(e)
มีการกำหนดดังต่อไปนี้:
- ถ้าe
เป็น unparenthesized ID-การแสดงออกหรือการเข้าถึงสมาชิกระดับ unparenthesized (5.2.5) เป็นประเภทของกิจการตั้งชื่อโดยdecltype(e)
e
หากไม่มีเอนทิตีดังกล่าวหรือe
ตั้งชื่อชุดของฟังก์ชันที่โอเวอร์โหลดแสดงว่าโปรแกรมมีรูปแบบไม่ถูกต้อง
- มิฉะนั้นถ้าe
เป็น Xvalue, decltype(e)
เป็นT&&
ที่T
เป็นประเภทของe
;
- มิฉะนั้นถ้าe
เป็น lvalue, decltype(e)
เป็นT&
ที่T
เป็นประเภทของe
;
- มิฉะนั้นdecltype(e)
เป็นประเภทของe
.
ตัวถูกดำเนินการของตัวระบุประเภทปฏิเสธคือตัวถูกดำเนินการที่ไม่ได้ประเมิน (ข้อ 5) [ตัวอย่าง:
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0;
decltype(i) x2;
decltype(a->x) x3;
decltype((a->x)) x4 = x3;
- ตัวอย่าง] [หมายเหตุ: กฎสำหรับการกำหนดประเภทที่เกี่ยวข้อง
decltype(auto)
ระบุไว้ใน 7.1.6.4 - ส่งหมายเหตุ]
กฎสำหรับdecltype(auto)
มีความหมายคล้ายกันสำหรับวงเล็บเสริมใน RHS ของนิพจน์การเริ่มต้น นี่คือตัวอย่างจากคำถามที่พบบ่อยเกี่ยวกับC ++และคำถามและคำตอบที่เกี่ยวข้อง
decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); }
ผลตอบแทนแรกstring
ผลตอบแทนที่สองซึ่งมีการอ้างอิงถึงตัวแปรท้องถิ่นstring &
str
การป้องกันข้อผิดพลาดที่เกี่ยวข้องกับมาโครตัวประมวลผลล่วงหน้า
มีโฮสต์ของรายละเอียดปลีกย่อยที่มีมาโครตัวประมวลผลล่วงหน้าในการโต้ตอบกับภาษา C ++ ที่เหมาะสมซึ่งส่วนใหญ่จะแสดงอยู่ด้านล่าง
- ใช้วงเล็บรอบพารามิเตอร์มาโครภายในนิยามมาโคร
#define TIMES(A, B) (A) * (B);
เพื่อหลีกเลี่ยงการมีลำดับความสำคัญของตัวดำเนินการที่ไม่ต้องการ (เช่นTIMES(1 + 2, 2 + 1)
ให้ผล 9 แต่จะให้ผล 6 โดยไม่มีวงเล็บรอบ(A)
และ(B)
- ใช้วงเล็บรอบอาร์กิวเมนต์มาโครที่มีเครื่องหมายจุลภาคอยู่ภายใน
assert((std::is_same<int, int>::value));
ซึ่งจะไม่สามารถรวบรวมได้
- ใช้วงเล็บรอบฟังก์ชันเพื่อป้องกันการขยายมาโครในส่วนหัวที่รวมไว้:
(min)(a, b)
(ด้วยผลข้างเคียงที่ไม่ต้องการจากการปิดใช้งาน ADL ด้วย)
&(C::f)
ตัวถูกดำเนินการ&
จะยังคงC::f
อยู่ใช่หรือไม่