最も厄介な構文解析

Syntactic ambiguity in C++

C++プログラミング言語において、最も厄介な構文解析は、直感に反する構文上の曖昧さの解決方法です。特定の状況では、C++の文法はオブジェクトパラメータの作成関数の型の指定を区別できません。そのような状況では、コンパイラは後者として行を解釈する必要があります

発生

「最も厄介な解析」という用語は、スコット・マイヤーズが2001年の著書『Effective STL』で初めて使用しました。[1] C 言語では珍しい現象でしたが、C++ではC++11統一初期化が導入されるまでは非常に一般的でした[2]

C言語スタイルのキャスト

関数型キャストが変数を初期化するための式を変換することを意図している場合、簡単な例が現れます

void f ( double my_dbl ) {   
    int i ( int ( my_dbl )); 
}

上記の2行目は曖昧です。1つの解釈としては、変換することによって生成された初期値を持つ変数を宣言することです。しかし、C言語では 関数のパラメータ宣言を囲む余分な括弧が許可されています。この場合、 の宣言は、代わりに次の関数宣言と同等になります i my_dblinti

// i という名前の関数は整数を受け取り、整数を返します。int 
i ( int my_dbl ) ;  

名前のない一時

より複雑な例は次のとおりです。

クラスTimer {  
    // ...
};

TimeKeeperクラス{  
パブリック:
    明示的なTimeKeeper (タイマーt ) ;  
    int getTime (); 
};

int main () {  
    TimeKeeper time_keeper ( Timer ()); 
    time_keeper.get_time ()返します。 
}

この行は

    TimeKeeper time_keeper ( Timer ()); 

曖昧です。なぜなら、次のようにも解釈できるからです

  1. クラス の変数の変数定義クラス の匿名インスタンスまたはtime_keeperTimeKeeperTimer
  2. 型のオブジェクトを返し、単一の(名前のない)パラメータを持ち、その型が入力を取らずにオブジェクトを返す関数(へのポインタ) [注 1]である関数宣言time_keeperTimeKeeperTimer

C ++標準では2番目の解釈が求められますが、これは後続の13行目とは矛盾しています。例えば、Clang++は12行目に最も厄介な解析が適用され、後続の13行目にエラーが発生していることを警告します。[3]

$ clang++ time_keeper.cc
timekeeper.cc:9:25:警告:括弧は関数宣言として曖昧さが解消されました
      [-Wvexing-parse] 
  ^~~~~~~~~ timekeeper.cc:9:26: 注記:変数を宣言するには括弧のペアを追加してください
  ^ ( ) timekeeper.cc:10:21:エラー:メンバー参照の基本型 ' ' は構造体または共用体ではありません~~~~~~~~~~~^~~~~~~~~TimeKeeper time_keeper(Timer());
                        
TimeKeeper time_keeper(Timer());
                         
                         
TimeKeeper (Timer (*)())
      
  return time_keeper.get_time();
         

解決策

これらの曖昧な宣言の解釈は、意図したものと一致することはほとんどありません。[4] [5] C++の関数型は通常、 typedefの背後に隠されており、明示的な参照またはポインタ修飾子が付けられるのが一般的です。別の解釈を強制するための典型的な手法は、異なるオブジェクト作成または変換構文を使用することです

型変換の例では、キャストに2つの代替構文が利用可能です。「Cスタイルのキャスト」

// int 型の変数を宣言します
int i (( int ) my_dbl ); 

または名前付きキャスト:

int i ( static_cast < int > ( my_dbl )); 

C でも有効な別の構文は、変数を初期化するときに = を使用することです。

int i = int ( my_dbl );   

変数宣言の例では、C++11以降の代替方法として、均一な(中括弧)初期化があります。[6] これにより、型名を完全に省略することもできます。

//次のいずれかの作業: 
TimeKeeper time_keeper ( Timer {}); TimeKeeper time_keeper { Timer ()}; TimeKeeper time_keeper { Timer {}}; TimeKeeper time_keeper ( {}); TimeKeeper time_keeper { {}}; 
 
 
      
      

C++11より前では、意図した解釈を強制するための一般的な手法は、余分な括弧の使用やコピー初期化でした。[5]

TimeKeeper time_keeper ( /*MVP を避ける*/ ( Timer ()) ); TimeKeeper time_keeper = TimeKeeper ( Timer ());    
   

後者の構文では、コピー初期化はコンパイラによって最適化される可能性が高い。 [7] C++17 以降では、この最適化は保証されている。[8]

注釈

  1. ^ C++の型減衰規則によれば、引数として宣言された関数オブジェクトは、その型の関数へのポインタと同等です。関数オブジェクト#CおよびC++を参照 してください

参考文献

  1. ^ マイヤーズ、スコット(2001).効果的なSTL:標準テンプレートライブラリの活用を改善するための50の具体的な方法. アディソン・ウェズリー. ISBN 0-201-74962-9
  2. ^ Coffin, Jerry (2012年12月29日). 「C++ - 最も厄介なパースの目的は何ですか?」Stack Overflow . 2021年1月17日時点のオリジナルよりアーカイブ2021年1月17日閲覧
  3. ^ Lattner, Chris (2010年4月5日). 「Clangエラーリカバリの驚くべき偉業」. LLVMプロジェクトブログ. 最も厄介なパース. 2020年9月26日時点のオリジナルよりアーカイブ。 2021年1月17日閲覧
  4. ^ DrPizza; Prototyped; wb; euzeka; Simpson, Homer J (2002年10月). 「C++の「最も厄介な解析」」. ArsTechnica OpenForum . 2015年5月20日時点のオリジナルよりアーカイブ。 2021年1月17日閲覧
  5. ^ ab Boccara, Jonathan (2018年1月30日). 「最も厄介なパース:それを見分け、素早く修正する方法」Fluent C++ . 2021年11月25日時点のオリジナルよりアーカイブ。 2021年1月17日閲覧
  6. ^ Stroustrup, Bjarne (2016年8月19日). 「C++11 FAQ」www.stroustrup.com . 統一された初期化構文とセマンティクス。2021年8月20日時点のオリジナルよりアーカイブ2021年1月17日閲覧。
  7. ^ 「C++に関する神話と都市伝説」C++ FAQ。コピー省略とは何ですか?RVOとは何ですか?2021年1月17日時点のオリジナルよりアーカイブ。 2021年1月17日閲覧
  8. ^ Devlieghere, Jonas (2016年11月21日). 「Guaranteed Copy Elision」. Jonas Devlieghere . 2021年11月25日時点のオリジナルよりアーカイブ2021年1月17日閲覧。 ただし、Brand, C++ (2018-12-11) に記載されている注意事項に注意してください。「保証されたコピー省略はコピーを省略しない」Microsoft C++ チームブログ。2021年11月25日時点のオリジナルからのアーカイブ。 2021年1月17日閲覧
  • C++03標準最終草案における議論(§8.2 曖昧さの解決[dcl.ambig.res]を参照): https://web.archive.org/web/20141113085328/https://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf
  • 直接初期化に関する CppReference(最も厄介な解析に対して脆弱な種類): https://en.cppreference.com/w/cpp/language/direct_initialization 2021 年 12 月 13 日にWayback Machineにアーカイブ
Retrieved from "https://en.wikipedia.org/w/index.php?title=Most_vexing_parse&oldid=1322646595"