TL; DR
余分な括弧は、次のコンテキストでC ++プログラムの意味を変更します。
- 引数に依存する名前の検索を防ぐ
- リストのコンテキストでカンマ演算子を有効にする
- 厄介な解析のあいまいさの解決
decltype
式の参照を推定する
- プリプロセッサマクロエラーの防止
引数に依存する名前検索の防止
標準の附属書Aに詳述されているようpost-fix expression
に、フォームの(expression)
a primary expression
はですが、id-expression
ではありませんunqualified-id
。つまり、フォームの関数呼び出しでは(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
}
—例を終了]
リストコンテキストでのカンマ演算子の有効化
コンマ演算子は、ほとんどのリストのようなコンテキスト(関数とテンプレートの引数、初期化リストなど)では特別な意味があります。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);
3つの引数があり、2番目の引数の値は5です。—例を終了]
厄介な解析のあいまいさの解決
Cおよびその難解な関数宣言構文との下位互換性により、vexing解析と呼ばれる驚くべき解析のあいまいさが生じる可能性があります。基本的に、宣言として解析できるものはすべて、1つのものとして解析されます。競合する解析も適用ます。
6.8あいまいさの解決[stmt.ambig]
1 式ステートメントと宣言を含む文法にはあいまいさがあります。左端の部分式が関数スタイルの明示的な型変換(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)); // function declaration
S x(int()); // function declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
—例を終了]
これの有名な例は、Most Vexing Parseです。これは、Scott Meyersが彼のEffective STL本のItem 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>
。関数データは2つのパラメーターを取ります。
- 最初のパラメーターの名前は
dataFile
です。タイプはistream_iterator<int>
です。かっこdataFile
は不要で無視されます。
- 2番目のパラメーターには名前がありません。その型は、何も取らずにを返す関数へのポインタ
istream_iterator<int>
です。
最初の関数の引数の周りに余分な括弧を配置すると(2番目の引数の周りの括弧は不正です)、あいまいさを解決します
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式または括弧なしのクラスメンバーアクセス(5.2.5)である場合、decltype(e)
はによって名前が付けられたエンティティのタイプですe
。そのようなエンティティが存在しない場合、またはe
オーバーロードされた関数のセットに名前を付けている、プログラムの形式は正しくありません。
—それ以外の場合、e
がxvalue、decltype(e)
is T&&
である場合、はのT
タイプですe
。
—それ以外の場合、e
が左辺値の場合、decltype(e)
is 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 ++よくある質問とその関連Q&Aは、
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
、2番目は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)
オペランドは&
isですがC::f
、そうではありませんか?