27

C++ の問題の 1 つは、テンプレートとテンプレート メタプログラミングを集中的に使用するコードから得られる恐ろしいエラー メッセージです。コンセプトはこの問題を解決するように設計されていますが、残念ながら次の標準には含まれません。

この問題は、ジェネリック プログラミングをサポートしているすべての言語に共通しているのでしょうか。または、C++ テンプレートに何か問題がありますか?

残念ながら、ジェネリック プログラミングをサポートする他の言語は知りません (Java と C# のジェネリックは単純すぎて、C++ テンプレートほど強力ではありません)。

D、Ada、Eiffel テンプレート (ジェネリック) もそのような醜いエラー メッセージを生成しますか? また、強力なジェネリック プログラミング パラダイムを備えた言語で、醜いエラー メッセージを表示しない言語を作成することは可能ですか? はいの場合、これらの言語はこの問題をどのように解決していますか?

編集:反対票を投じた人向け。C++ とテンプレートが大好きです。テンプレートが悪いと言っているのではありません。実際、私はジェネリック プログラミングとテンプレート メタプログラミングの大ファンです。コンパイラからこのような醜いエラー メッセージが表示される理由を尋ねているだけです。

4

6 に答える 6

18

一般的に、ジェネリックの Ada コンパイラ エラー メッセージは、他のどの Ada コンパイラ エラー メッセージよりも読みにくいということはありませんでした。

一方、C++ テンプレートのエラー メッセージは、エラー ノベルであることで有名です。私が思う主な違いは、C++ がテンプレートのインスタンス化を行う方法です。問題は、C++ テンプレートは Ada ジェネリックよりもはるかに柔軟です。それは非常に柔軟で、ほとんどマクロ プリプロセッサに似ています。Boost の賢い人々はこれを使用して、ラムダやその他の言語全体を実装しています。

その柔軟性のため、基本的に、テンプレート パラメーターの特定の順列が最初に検出されるたびに、テンプレート階層全体を新たにコンパイルする必要があります。したがって、API のいくつかのレイヤーで非互換性に至るまで解決された問題は、最終的には貧弱な API クライアントに提示されて解読されてしまいます。

Ada では、ジェネリックは実際には強く型付けされており、通常のパッケージやサブルーチンと同様に、完全な情報をクライアントに隠しています。したがって、エラー メッセージが表示された場合は、通常、それを実装するために使用された階層全体ではなく、インスタンス化しようとしているジェネリックを参照しているだけです。

そうです、C++ テンプレートのエラー メッセージは、Ada のエラー メッセージよりもずっとひどいものです。

デバッグはまったく別の話です...

于 2011-04-04T15:52:07.840 に答える
17

根本的な問題は、状況に関係なく、エラーの回復が難しいことです。

そして、C や C++ の恐ろしい文法を考慮に入れると、エラー メッセージがそれよりも悪くないことに驚くだけです! 残念ながら、C 文法は、文法の本質的な特性について手がかりを持たない人々によって設計されたのではないでしょうか。できるだけ明確に。

一般的なエラーを説明しましょう: セミコロンの忘れです。

struct CType {
  int a;
  char b;
}
foo
bar() { /**/ }

わかりました、これは間違っています。行方不明のセミコロンはどこに行けばよいでしょうか? 残念ながら、あいまいです。次のfoo理由により、前または後に移動できます。

  • C では、変数を定義した後、ストライドで変数を宣言するのが通常と見なされます。struct
  • C では、関数の戻り値の型を指定しないのが普通と見なされます (その場合、デフォルトは になりますint) 。

推論すると、次のことがわかります。

  • 型に名前を付ける場合foo、それは関数宣言に属します
  • そうでない場合は、おそらく変数を示しています...もちろん、タイプミスをして、それが書かれることを意図していた場合を除いて、foolそれはたまたま型です:/

おわかりのように、エラーの回復は非常に困難です。なぜなら、書き手の意図を推測する必要があり、文法が受容的とはほど遠いからです。不可能ではありませんが、ほとんどのエラーは多かれ少なかれ正確に診断でき、回復することさえできます...かなりの労力が必要です。

に取り組んでいる人々は、高速gccなコードを生成すること(gcc 4.6 で最新のベンチマークを検索することを意味します) と興味深い機能を追加することに関心があるようです (gcc は、C++0x のすべてではないにしてもほとんどを実装しています)。エラーメッセージが読みやすい。あなたは彼らを責めることができますか?私はできません。

幸いなことに、正確なエラー報告と優れたエラー回復が非常に価値のある目標であると考えている人がいます。その中には、かなり長い間 CLang に取り組んできた人もいます。

私の頭の上からいくつかの素晴らしい機能:

  • エラーがどこから発生したかを正確に明らかにするためのソース範囲を含む、簡潔だが完全なエラーメッセージ
  • Fix-Itは、意図されたことが明らかな場合に記録します
  • その場合、コンパイラは、意味不明な行に行を吐き出す代わりに、修正が既に存在するかのようにファイルの残りを解析します。
  • (最近) ノートにインクルード スタックを含めるのを避けて、クラフトを切り取ります。
  • (最近) 開発者が実際に書いたテンプレート パラメーターの型のみを公開しようとし、typedef を保持します (したがって、どちらがすべての違いを生むかについて話std::vector<Name>std::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> >ます)
  • template(最近)別のテンプレートメソッド内からのテンプレートメソッドへの呼び出しで欠落している場合に欠落した場合に正しく回復する

しかし、それぞれに数時間から数日かかる作業が必要です。

彼らは確かにタダではありませんでした。

さて、概念は (通常) 私たちの生活を楽にしてくれるはずです。しかし、それらはほとんどテストされていなかったため、ドラフトから削除することが望ましいと見なされました。これは嬉しいと言わざるを得ません。C++ の相対的な慣性を考えると、完全に改訂されていない機能を含めないほうがよいでしょう。また、コンセプト マップにはあまり感心しませんでした。Bjarne も Herb も、次の標準に向けて概念をゼロから再考すると言ったように、興奮していないようです。

于 2011-04-03T15:16:40.480 に答える
11

ジェネリック プログラミングの記事では、特に Ada を含むいくつかの言語でジェネリックの長所と短所の多くを概説しています。テンプレートの特殊化は欠けていますが、すべてのAda ジェネリック インスタンスは「インスタンス宣言と同等であり、直後にインスタンス本体が続きます」。実際問題として、エラー メッセージはコンパイル時に発生する傾向があり、通常、よく知られているタイプ セーフ違反を表します。

于 2011-04-03T20:27:05.887 に答える
9

Dには、テンプレートエラーメッセージの品質を向上させるための2つの機能があります。制約とstatic assert

// Use constraints to only allow a function to operate on random access 
// ranges as defined in std.range.  If something that doesn't satisfy this
// is passed, the compiler will error before even trying to instantiate
// fun().
void fun(R)(R range) if(isRandomAccessRange!(R)) {
    // Do stuff.
}


// Use static assert to check a high level invariant.  If 
// the predicate is false, the error message will be 
// printed and compilation will stop before a screen 
// worth of more confusing errors are encountered.
// This function takes any number of ranges to merge sort
// and the same number of temporary buffers to merge into.
void mergeSort(R...)(R ranges) {
    static assert(R.length % 2 == 0, 
        "Must have equal number of ranges to be sorted and temporary buffers.");

    static assert(allSatisfy!(isRandomAccessRange, R), 
        "All arguments to mergeSort must be random access ranges.");

    // Implementation
}
于 2011-04-03T14:26:55.610 に答える
5

Eiffel はすべてのテンプレート システムの中で最も優れているため、すべてのエラー メッセージの中で最も優れています。これは言語に完全に統合されており、引数で covarianz を使用している唯一の言語であるため、うまく機能します。

したがって、単純なコンパイラのコピー アンド ペースト以上のものです。残念ながら、違いを数行で説明することは不可能です。EiffelStudio に行って見てください。

于 2011-04-20T00:52:29.837 に答える
2

エラー メッセージを改善するためのいくつかの取り組みがあります。たとえば、Clangは、より読みやすいコンパイラ エラー メッセージを生成することに重点を置いています。私はそれを少しの間しか使用していませんが、これまでの私の経験は、GCC の同等のエラーと比較して非常に肯定的です。

于 2011-04-03T11:56:22.657 に答える