演算子の優先順位以外に、余分な括弧はいつ効果がありますか?


91

C ++の括弧は多くの場所で使用されます。たとえば、関数の呼び出しやグループ化式で演算子の優先順位を上書きします。不正な余分な括弧(関数呼び出しの引数リストの周りなど)を除いて、C ++の一般的な(ただし絶対ではない)規則は、余分な括弧が害を及ぼすことはありません

5.1一次式[expr.prim]

5.1.1一般[expr.prim.general]

6括弧で囲まれた式は、型と値が囲まれた式と同じである1次式です。括弧の存在は、式が左辺値かどうかには影響しません。括弧で囲まれた式は、別段の指示がない限り、囲まれた式を使用できるのとまったく同じコンテキストで、同じ意味で使用できます

質問:基本的な演算子の優先順位を上書きする以外に、余分な括弧がC ++プログラムの意味を変更するのはどのコンテキストですか?

:意味の異なる2つの構文を許可するのではなく、構文を制限するので、括弧なしのメンバーへポインター構文の制限&qualified-idは範囲外と見なします。同様に、プリプロセッサマクロ定義内括弧を使用すると、不要な演算子の優先順位を防ぐことができます。


「私は、メンバーへのポインターへの&(qualified-id)解決を、演算子の優先順位の適用と考えています。」 - 何故ですか?で括弧を省略しても、&(C::f)オペランドは&isですがC::f、そうではありませんか?

@hvd expr.unary.op/4:メンバーへのポインターは、明示&が使用され、そのオペランドが括弧で囲まれていない修飾IDである場合にのみ形成されます。
TemplateRex

そうですね、それは演算子の優先順位とどう関係しているのでしょうか?(

@hvdが更新されました。このQ&AでRHSとLHSを混同しており、括弧を使用し()て、メンバーへのポインターセレクターに対する関数呼び出しの優先順位を上書きしています::*
TemplateRex

1
どのケースを検討する必要があるかについて、もう少し正確にする必要があると思います。たとえば、型名を括弧で囲んでCスタイルのキャスト演算子にする(コンテキストに関係なく)と、括弧で囲まれた式はまったく作成されません。一方、技術的に言え、かっこ式はifまたはwhileの後にかっこで囲まれた式ですが、かっこは構文の一部なので、ここでは考慮しません。かっこがないと、演算子の優先順位に関係なく、式が単一のユニットとして解析されなくなるIMOも当てはまりません。
Marc van Leeuwen、2014年

回答:


112

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も無効にするという望ましくない副作用があります)

7
プログラムの意味を実際に変更するわけではありませんが、ベストプラクティスであり、コンパイラーによって発行される警告に影響します。式が割り当てである場合は、if/で追加の括弧を使用する必要がありますwhile。例if (a = b)-警告(もしかして==?)、if ((a = b))while-警告なし。
Csq 2014年

@Csqありがとう、良い観察ですが、それは特定のコンパイラによる警告であり、規格では義務付けられていません。これはこのQ&Aの言語弁護士の性質に適合しないと思います。
TemplateRex

DOES (min)(a, b)(邪悪なマクロではmin(A, B))引数依存名のルックアップ防止の一部ですか?
Jarod42 2014年

@ Jarod42そうだと思いますが、そのようなマクロや他の邪悪なマクロは質問の範囲外である考えてみましょう:-)
TemplateRex

5
@JamesKanze:OPとTemplateRexは同じ人物です^ _ ^
Jarod42

4

一般に、プログラミング言語では、「余分な」括弧は、構文解析の順序や意味を変更しないことを意味します。それらは、コードを読む人々の利益のために順序(演算子の優先順位)を明確にするために追加されており、その唯一の効果は、コンパイルプロセスをわずかに遅くし、コードを理解する際の人的エラーを減らすことです(おそらく開発プロセス全体をスピードアップします)。 )。

括弧のセットが実際に式の解析方法を変更する場合、それらは定義上余分ではありません。不正または無効な解析を正当なものに変換する括弧は「余分」ではありませんが、言語のデザインが悪いことを指摘している場合があります。


2
正確に、そして特に明記しない限り、これはC ++でも一般的な規則です(質問の標準的な引用を参照)。これらの「弱点」を指摘することが、このQ&Aの目的でした。
TemplateRex
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.