9

テンプレート プログラミングでstatic_assertは、プログラマがテンプレート引数の制約をチェックし、制約違反時に人間が読めるエラー メッセージを生成するのに役立ちます。

このコードを考えてみましょう。

template<typename T>
void f(T)
{
    static_assert(T(), "first requirement failed to meet.");

    static_assert(T::value, "second requirement failed to meet.");    

    T t = 10; //even this may generate error!
}

私の考えは、最初のエラーが発生した場合、いくつかの要件が満たされていないstatic_assertことを意味するため、コンパイルを停止して、最初のエラー メッセージのみを生成する必要があるということです。エラー メッセージのほとんどは単一制約違反を示しています。たった 1 つではなく、何百ものエラー メッセージが画面上で非常に恐ろしく見えますTstatic_assert

たとえば、上記の関数テンプレートを次のように呼び出すとします。

f(std::false_type{});

GCC 4.8 は以下を生成します。

main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:16:24:   required from here
main.cpp:7:5: error: static assertion failed: first requirement failed to meet.
     static_assert(T(), "first requirement failed to meet.");
     ^
main.cpp:9:5: error: static assertion failed: second requirement failed to meet.
     static_assert(T::value, "second requirement failed to meet.");    
     ^
main.cpp:11:11: error: conversion from 'int' to non-scalar type 'std::integral_constant<bool, false>' requested
     T t = 10; //even this may generate error!

ご覧のとおり (オンライン)、これはエラーが多すぎます。最初のコードが失敗した場合、コンパイルを続行すると残りのコードも失敗するstatic_assert可能性が非常に高いのに、なぜコンパイルを続行するのでしょうか? テンプレート プログラミングでは、多くのプログラマーがこのようなカスケード エラー メッセージを望んでいないと確信しています。

次のように、関数を複数の関数に分割し、それぞれが1つの制約のみをチェックすることで、この問題を解決しようとしました。

template<typename T>
void f_impl(T); //forward declaration

template<typename T>
void f(T)
{
    static_assert(T(), "first requirement failed to meet.");
    f_impl(T());
}

template<typename T>
void f_impl(T)
{
    static_assert(T::value, "second requirement failed to meet.");     
    T t = 10;
}  

f(std::false_type{}); //call

これにより、次が生成されます。

main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:24:24:   required from here
main.cpp:10:5: error: static assertion failed: first requirement failed to meet.
     static_assert(T(), "first requirement failed to meet.");
     ^

これは大幅な改善です。1 つのエラー メッセージだけで、読みやすく、理解しやすくなっています (オンラインを参照)。

私の質問は、

  • コンパイルが最初に停止しないのはなぜstatic_assertですか?
  • 関数テンプレートを分割し、各 function_impl で 1 つの制約をチェックすると、 GCCのみが役立ち、clangは依然として 多くのエラーを生成するため、より一貫した方法で診断を改善する方法はありますか? すべてのコンパイラで機能するものはありますか?
4

2 に答える 2

4

ここでは、バランスを取る必要がある複数の目標があります。特に、最初のエラーで停止することにより、小さくて単純なエラー メッセージが表示される場合があります。これは良いことです。同時に、最初のエラーで停止しても、コストがかかる可能性のある別のコンパイルを試行する前に解決する必要のある他の問題に関する情報は得られません。たとえば、最初の例では、すべてのstatic_asserts を一度にチェックすることを個人的に好みます。エラー メッセージを次のように読みます。

次の要件を満たしていません:

  • デフォルトのコンストラクタ
  • 入れ子value

1 つを修正してビルド システムが次のエラーでトリップするまで数分かかるよりも、最初のパスでこれらの両方のエラーを検出したいと思います。

ここでの前提は、コンパイラがエラーから回復して解析を続行できることですが、文法はコンテキスト依存であり、常にそうであるとは限りません。したがって、問題のマイナス面の一部は、最初のエラーを信頼できることですが、次のエラーは最初のエラーの結果にすぎない可能性があり、どれがどれであるかを理解するには経験が必要です。

これはすべて実装の品質 (したがってコンパイラに依存) であり、多くの実装ではいつ停止するかを決定できるため、ユーザーとコンパイラに渡されるフラグに依存します。コンパイラは、エラーの報告とエラーからの回復が改善されているため、ここでの改善が期待できます。さらに改善するために、 > C++14 (C++17? 以降?) では、エラー メッセージを改善するための概念が追加されます。

要約:

  • これは実装の品質であり、コンパイラ フラグで制御できます。
  • 誰もがあなたの望むものを望んでいるわけではありません.各コンパイラパスで複数のエラーを検出したい人もいます.
  • 将来的にはエラー メッセージが改善される予定です (概念、コンパイラの改善)
于 2013-12-02T19:22:51.143 に答える
4

私は David Rodríguez に同意します - dribeas とコンパイラ ライターを擁護するために、次の例を検討してください。

#include <type_traits>

class A {};

// I want the nice error message below in several functions.
// Instead of repeating myself, let's put it in a function.
template <typename U>
void check() {
    static_assert(std::is_convertible<U*, const volatile A*>::value,
        "U doesn't derive publicly from A "
        "(did you forget to include it's header file?)");
}

template <typename U>
void f(U* u) {
    // check legality (with a nice error message)
    check<U>();
    // before trying a failing initialization:
    A* p = u;
}

class B; // I forget to include "B.h"

int main() {
    B* b = nullptr;
    f(b);
}

のインスタンス化がf<B>始まると、コンパイラ (またはコンパイラ ライター) は次のように考えるかもしれませんcheck<U>。をインスタンス化する必要はありませんcheck。」

上記の理由は理にかなっていると思います。(私はコンパイラ ライターではないので、ここでは憶測に過ぎないことに注意してください)。

GCC 4.8 と VS2010 の両方がコンパイルを続けf<B>、インスタンス化check<B>を後で延期します。次に、失敗した初期化を見つけて、独自のエラー メッセージを提供します。VS2010 がすぐに停止し、素敵なエラー メッセージが表示されません。GCC は続行し、私が望んでいたメッセージを生成します (ただし、GCC 自身の後でのみ)。

メタプログラミングは、プログラマーにとってもコンパイラーにとってもトリッキーです。static_assert非常に役立ちますが、万能薬ではありません

于 2013-12-02T20:00:44.177 に答える