TL; DR
Tanda kurung tambahan mengubah arti program C ++ dalam konteks berikut:
- mencegah pencarian nama yang bergantung pada argumen
- mengaktifkan operator koma dalam konteks daftar
- resolusi ambiguitas dari parsing yang mengganggu
- menyimpulkan referensi dalam
decltype
ekspresi
- mencegah kesalahan makro preprocessor
Mencegah pencarian nama yang bergantung pada argumen
Seperti yang dijelaskan dalam Lampiran A Standar, post-fix expression
bentuk (expression)
a adalah a primary expression
, tetapi bukan an id-expression
, dan oleh karena itu bukan an unqualified-id
. Ini berarti bahwa pencarian nama yang bergantung pada argumen dicegah dalam pemanggilan fungsi dari formulir (fun)(arg)
dibandingkan dengan formulir konvensional fun(arg)
.
3.4.2 Pencarian nama yang bergantung pada argumen [basic.lookup.argdep]
1 Ketika ekspresi-postfix dalam panggilan fungsi (5.2.2) adalah id tanpa kualifikasi , ruang nama lain yang tidak dipertimbangkan selama pencarian biasa yang tidak memenuhi syarat (3.4.1) dapat dicari, dan di ruang nama tersebut, fungsi teman lingkup namespace atau deklarasi template fungsi (11.3) yang tidak terlihat dapat ditemukan. Modifikasi pada pencarian ini bergantung pada tipe argumen (dan untuk template template argumen, namespace argumen template). [Contoh:
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s);
(f)(s);
}
—Dan contoh]
Mengaktifkan operator koma dalam konteks daftar
Operator koma memiliki arti khusus di sebagian besar konteks seperti daftar (argumen fungsi dan templat, daftar penginisialisasi, dll.). Tanda kurung formulir a, (b, c), d
dalam konteks seperti itu dapat mengaktifkan operator koma dibandingkan dengan formulir biasa di a, b, c, d
mana operator koma tidak berlaku.
5.18 Operator koma [expr.comma]
2 Dalam konteks di mana koma diberi arti khusus, [Contoh: dalam daftar argumen ke fungsi (5.2.2) dan daftar penginisialisasi (8.5) —dan contoh] operator koma seperti yang dijelaskan dalam Klausul 5 hanya dapat muncul dalam tanda kurung. [Contoh:
f(a, (t=3, t+2), c);
memiliki tiga argumen, yang kedua memiliki nilai 5. —sudah contoh]
Resolusi ambiguitas dari parsing yang mengganggu
Kompatibilitas mundur dengan C dan sintaks deklarasi fungsi rahasia dapat menyebabkan ambiguitas penguraian yang mengejutkan, yang dikenal sebagai penguraian menjengkelkan. Pada dasarnya, apa pun yang dapat diuraikan sebagai deklarasi akan diuraikan sebagai satu deklarasi , meskipun parse yang bersaing juga akan diterapkan.
6.8 Resolusi ambiguitas [stmt.ambig]
1 Ada ambiguitas dalam tata bahasa yang melibatkan pernyataan-ekspresi dan deklarasi : Pernyataan-ekspresi dengan konversi tipe eksplisit gaya fungsi (5.2.3) sebagai subekspresi paling kiri dapat dibedakan dari deklarasi di mana deklarator pertama dimulai dengan a ( . dalam kasus-kasus pernyataan itu adalah sebuah deklarasi .
8.2 Resolusi ambiguitas [dcl.ambig.res]
1 Ambiguitas yang timbul dari kesamaan antara cor gaya-fungsi dan deklarasi yang disebutkan dalam 6.8 juga dapat terjadi dalam konteks deklarasi . Dalam konteks itu, pilihannya adalah antara deklarasi fungsi dengan sekumpulan tanda kurung yang berlebihan di sekitar nama parameter dan deklarasi objek dengan cast gaya fungsi sebagai penginisialisasi. Sama seperti ambiguitas yang disebutkan dalam 6.8, penyelesaiannya adalah dengan mempertimbangkan setiap konstruksi yang mungkin merupakan deklarasi sebagai deklarasi . [Catatan: Deklarasi dapat secara eksplisit disingkirkan oleh cast bergaya nonfungsi, dengan = untuk menunjukkan inisialisasi atau dengan menghapus tanda kurung yang berlebihan di sekitar nama parameter. —Kirim catatan] [Contoh:
struct S {
S(int);
};
void foo(double a) {
S w(int(a));
S x(int());
S y((int)a);
S z = int(a);
}
—Dan contoh]
Contoh terkenal dari ini adalah Most Vexing Parse , nama yang dipopulerkan oleh Scott Meyers di Item 6 dari buku Effective STL-nya :
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
istream_iterator<int>());
Ini mendeklarasikan sebuah fungsi data
, yang tipe kembaliannya adalah list<int>
. Data fungsi mengambil dua parameter:
- Parameter pertama diberi nama
dataFile
. Jenisnya adalah istream_iterator<int>
. Tanda kurung di sekitarnya dataFile
tidak berguna dan diabaikan.
- Parameter kedua tidak memiliki nama. Jenisnya adalah pointer untuk berfungsi tidak mengambil apa pun dan mengembalikan file
istream_iterator<int>
.
Menempatkan tanda kurung tambahan di sekitar argumen fungsi pertama (tanda kurung di sekitar argumen kedua adalah ilegal) akan menyelesaikan ambiguitas
list<int> data((istream_iterator<int>(dataFile)),
istream_iterator<int>());
C ++ 11 memiliki sintaks brace-initializer yang memungkinkan untuk mengesampingkan masalah penguraian seperti itu dalam banyak konteks.
Mengurangi referensi dalam decltype
ekspresi
Berbeda dengan auto
deduksi tipe, decltype
memungkinkan referensi (referensi nilai l dan nilai r) untuk disimpulkan. Aturan membedakan antara decltype(e)
dan decltype((e))
ekspresi:
7.1.6.2 Penentu tipe sederhana [dcl.type.simple]
4 Untuk ekspresi e
, tipe yang dilambangkan dengandecltype(e)
didefinisikan sebagai berikut:
- jika e
adalah ekspresi-id yang tidak diberi tanda kurung atau akses anggota kelas yang tidak diberi tanda kurung (5.2.5), decltype(e)
adalah jenis entitas yang dinamai oleh e
. Jika tidak ada entitas seperti itu, atau jika e
menamai sekumpulan fungsi yang kelebihan beban, programnya salah format;
- sebaliknya, jika e
nilai x, decltype(e)
adalah T&&
, di mana T
jenis e
;
- sebaliknya, jika e
nilai l, decltype(e)
adalah T&
, di mana T
jenis e
;
- jika tidak, decltype(e)
adalah tipe e
.
Operand dari penentu jenis deklarasi adalah operan yang tidak dievaluasi (Klausul 5). [Contoh:
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;
—Dan contoh] [Catatan: Aturan untuk menentukan tipe yang melibatkan
decltype(auto)
ditentukan dalam 7.1.6.4. —Kirim catatan]
Aturan untuk decltype(auto)
memiliki arti yang sama untuk tanda kurung tambahan di kanan atas ekspresi inisialisasi. Berikut adalah contoh dari C ++ FAQ dan Q&A terkait ini
decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); }
Pengembalian pertama string
, pengembalian kedua string &
, yang merupakan referensi ke variabel lokal str
.
Mencegah kesalahan terkait makro praprosesor
Ada sejumlah kehalusan dengan makro praprosesor dalam interaksinya dengan bahasa C ++ yang sesuai, yang paling umum tercantum di bawah ini
- menggunakan tanda kurung di sekitar parameter makro di dalam definisi makro
#define TIMES(A, B) (A) * (B);
untuk menghindari prioritas operator yang tidak diinginkan (misalnya di TIMES(1 + 2, 2 + 1)
mana menghasilkan 9 tetapi akan menghasilkan 6 tanpa tanda kurung di sekitarnya (A)
dan(B)
- menggunakan tanda kurung di sekitar argumen makro yang memiliki koma di dalamnya:
assert((std::is_same<int, int>::value));
yang tidak dapat dikompilasi
- menggunakan tanda kurung di sekitar fungsi untuk melindungi dari ekspansi makro di header yang disertakan:
(min)(a, b)
(dengan efek samping yang tidak diinginkan juga menonaktifkan ADL)
&(C::f)
, operan dari&
masihC::f
, bukan?