web-dev-qa-db-ja.com

C ++テンプレートのエラーメッセージがそれほど恐ろしいのはなぜですか?

C++テンプレートは、長くて読めないエラーメッセージを生成することで有名です。 C++のテンプレートエラーメッセージがなぜそれほどひどいのか、という一般的な考えがあります。基本的に、問題は、コンパイラーがテンプレート内の特定のタイプでサポートされていない構文を検出するまでエラーがトリガーされないことです。例えば:

_template <class T>
void dosomething(T& x) { x += 5; }
_

Tが_+=_演算子をサポートしていない場合、コンパイラはエラーメッセージを生成します。また、これがライブラリ内のどこかで発生した場合、エラーメッセージは数千行になることがあります。

しかし、C++テンプレートは基本的に、コンパイル時のダックタイピングのメカニズムにすぎません。 C++テンプレートエラーは、概念的には、Pythonなどの動的言語で発生する可能性があるランタイム型エラーに非常に似ています。たとえば、次のPythonコードを考えてみます。

_def dosomething(x):
   x.foo()
_

ここで、xfoo()メソッドがない場合、Pythonインタープリターが例外をスローし、スタックトレースとかなり明確なエラーを表示します問題を示すメッセージ。インタプリタがライブラリ関数の奥深くに入るまでエラーがトリガーされない場合でも、ランタイムエラーメッセージは、通常のC++コンパイラによって吐き出された読み取り不可能な嘔吐ほど悪くはありません。 C++コンパイラーは何が問題であったかについてより明確ではないのですか?一部のC++テンプレートエラーメッセージが文字通りコンソールウィンドウを5秒以上スクロールさせるのはなぜですか?

31
Channel72

テンプレートのエラーメッセージは悪名高いかもしれませんが、必ずしも長くて読めないわけではありません。この場合、エラーメッセージ全体(gccから)は次のとおりです。

test.cpp: In function ‘void dosomething(T&) [with T = X]’:
test.cpp:11:   instantiated from here
test.cpp:6: error: no match for ‘operator+=’ in ‘x += 5’

Pythonの例のように、テンプレートのインスタンス化ポイントの「スタックトレース」と、問題を示す明確なエラーメッセージが表示されます。

時には、さまざまな理由により、テンプレート関連のエラーメッセージがはるかに長くなることがあります。

  • 「スタックトレース」はもっと深いかもしれません
  • テンプレートは他のテンプレートのインスタンス化を引数としてインスタンス化され、すべての名前空間修飾子とともに表示されるため、タイプ名ははるかに長くなる可能性があります。
  • オーバーロードの解決に失敗すると、エラーメッセージに候補となるオーバーロードのリストが含まれる場合があります(それぞれに非常に長い型名が含まれる場合があります)。
  • 無効なテンプレートが多くの場所でインスタンス化されている場合、同じエラーが何度も報告されることがあります

Python=との主な違いは静的型システムであり、エラーメッセージに(場合によっては長い)型名を含める必要があります。これらがないと、診断が非常に困難になることがあります- why過負荷の解決に失敗しました。これらを使用すると、問題はどこにあるかを推測するのではなく、どこにあるかを示す象形文字を解読することです。

また、実行時にチェックすると、プログラムは最初に発生したエラーで停止し、単一のメッセージのみを表示します。コンパイラーは、発生するすべてのエラーを、あきらめるまで表示します。少なくともC++では、ファイル内の最初のエラーで停止しないでください。これは、後のエラーの結果である可能性があるためです。

30
Mike Seymour

明らかな理由のいくつかは次のとおりです。

  1. 歴史。 gccやMSVCなどが新しい場合、より多くの余分なスペースを使用してデータを格納し、より適切なエラーメッセージを生成する余裕はありませんでした。記憶が不足していたので、彼らはそれをすることができませんでした。
  2. 何年もの間、消費者はエラーメッセージの品質を無視していたため、ベンダーもほとんどの場合そうしていました。
  3. 一部のコードでは、コンパイラーはコードの後半で実際のエラーを再同期して診断できます。テンプレートのエラーは非常にひどくカスケードされるため、最初のものを超えるものはほとんど役に立ちません。
  4. テンプレートの一般的な柔軟性により、コードにエラーが発生したときにおそらくが何を意味するのかを推測することが難しくなります。
  5. テンプレート内では、名前の意味はテンプレートのコンテキストとインスタンス化のコンテキストの両方に依存します。and引数に依存するルックアップにより、さらに多くの可能性が追加されます。
  6. 関数のオーバーロードは、特定の関数呼び出しmightが参照する候補のlotsを提供し、一部のコンパイラ(gccなど)は、あいまいな場合にそれらすべてを忠実にリストします。
  7. 渡された値が要件を満たしていることを確認せずに通常のパラメーターを使用することを決して考えなかった多くのコーダーは、テンプレートパラメーターをチェックすることすらしません(そして、私は告白しなければなりません。

それは網羅的ではありませんが、あなたは一般的な考えを理解します。簡単ではないにしても、ほとんどは治ります。何年もの間、私は人々に定期的に使用するためにコモーC++のコピーを入手するように言ってきました。たぶん、コンパイラーの代金を払うために、1つのエラーメッセージから1回は十分に節約できました。現在、Clangは同じ点に到達しています(そして、それはさらに安価です)。

最後に、冗談のように聞こえるかもしれませんが、実際はそうではない一般的な所見をまとめます。ほとんどの場合、ソースコードをエラーメッセージに変換するコンパイラの実際の仕事は正直なところisです。ベンダーがその仕事をもう少しよくすることに集中している時期です-私がコンパイラーを書いたとき、私はそれを二次的(せいぜい)として扱う傾向が強く、場合によってはそれをほとんど無視したことを公然と認めます完全に。

14
Jerry Coffin

簡単な答えは、Pythonはそのように機能するように設計されているためですが、テンプレートに関連する多くのものが偶然に生じたものです。それ自体がチューリング完全なシステムになることは意図されていませんでした。たとえば、システムが意図的に計画されて、何が起こるかについて推論できない場合works何かがうまくいかなかったときに何が起こるかについて、慎重で思慮深い計画を誰もが期待する必要があるのはなぜですか。

また、あなたが指摘したように、Pythonインタプリタは、Pythonコードを解釈しているため、スタックトレースを表示することにより、はるかに簡単になります。C++の場合コンパイラーがテンプレートエラーにヒットし、スタックトレースを提供します。これは、「テンプレートの嘔吐」と同じくらい役に立ちませんよね。

9
Mason Wheeler