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


28

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秒以上スクロールするのはなぜですか?


6
一部のコンパイラには恐ろしいエラーメッセージがありますが、他のコンパイラは本当に優れています(clang++wink wink)。
ベンジャミンバニエ

2
したがって、コンパイル時に失敗するのではなく、実行時に失敗し、出荷され、顧客の手でプログラムが失敗することを好むでしょうか?
P Shved

13
@Pavel、いいえ。この質問は、ランタイムとコンパイル時のエラーチェックの利点/欠点に関するものではありません。
Channel72

1
大型C ++テンプレートエラーの例として、FWIW:codegolf.stackexchange.com/a/10470/7174は
KEBS

回答:


28

テンプレートのエラーメッセージは悪名高いかもしれませんが、決して長くて読めないというわけではありません。この場合、エラーメッセージ全体(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との主な違いは静的型システムであり、エラーメッセージに(時には長い)型名を含める必要が生じます。それらがないと、オーバーロード解決が失敗した理由を診断することが非常に困難になる場合があります。それらを使用すると、問題はどこにあるかを推測することではなく、それがどこにあるかを示す象形文字を解読することです。

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


4
後のエラーの結果であるエラーの例を挙げていただけますか?
ルスラン

12

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

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

これは完全なものではありませんが、一般的なアイデアは得られます。たとえそれが簡単でなくても、ほとんどは治ることができます。何年もの間、私は人々に定期的に使用するためにComeau C ++のコピーを入手するように言ってきました。私はおそらく、コンパイラーの費用を支払うために、1つのエラーメッセージから一度に十分に保存したでしょう。現在、Clangは同じポイントに到達しています(さらに安価です)。

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


9

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.