3

解析目的で、テンプレート識別子リストの内容をスキップしたいとしましょう:

template<(valid code)>
        ^            ^
        | from       | to

最初に頭に浮かぶのは、盲目的に最初の > を見つけることですが、それが常に機能するとは限りません。

template<(valid code),template<(valid code)> >
        ^                                  ^
        | from                             | to, oops

より良いアプローチは、再帰的に < と > のペアをスキップすることです:

template<(valid code),template<(valid code)> >
        ^                                    ^
        | from                               | to, better

ただし、このアプローチでさえ、次のような難解だが有効な傑作には失敗します (bits\random.h、69 行目、GCC 4.7.x から):

template<(...),bool = __w < static_cast<size_t>(...)>
        ^                 ^            ^      ^     ^     ^
        | 1               | 2          | 3    | 2   | 1   | where did 0 go?

私の質問は、有効なテンプレート識別子リストの最後を見つける正しい方法は何ですか?

4

2 に答える 2

2

の例よりも病理学的な可能性がさらにあるため、有効なテンプレート識別子リストの終わりを見つける簡単な方法は実際にはありませんbits/random.h。(この回答の最後に病理学的ケースの例があります: Is C++ context-free or context-sensitive?では、大きな整数が素数かどうかに応じて識別子がテンプレートであるかどうかが決まります。)

アルゴリズムは簡単に記述できます (C++11 標準の §14.2 のパラグラフ 3 にあります[temp.names])。基本的に、 aは、テンプレート識別子、単語、または演算子の 1 つ<に続く場合にのみ、テンプレート ブラケットです。openを取得すると、括弧構文 (中かっこや角かっこを含む) 内にネストされておらず、ネストされたテンプレート ブラケットと一致しない最初のものと一致します。C++11 では、これにはトークンの一部であるものが含まれます。templatecast<>>>>

例として、 という例があったrandom.h場合bool = __w > ...、は>テンプレートの閉じ括弧と見なされます。しかし、これは で<あり__w、テンプレート識別子ではないため、小なり演算子として扱われます。私見の良いスタイルは、比較演算子とシフト演算子がテンプレート ブラケット内にある場合は常に括弧で囲むことですが、それは私だけです。

標準の正確な文言:

名前のルックアップで、名前がtemplate-nameであるか、operator-function-idまたはリテラル operator-idがオーバーロードされた関数のセットを参照していることがわかった後、そのメンバーのいずれかが関数テンプレートである場合、この後に が続き<ます<。常にtemplate-argument-listの区切り文字として使用され、小なり演算子として使用されることはありません。template-argument-list を解析するとき、ネストされていない最初の>ものが大なり演算子ではなく終了区切り文字として使用されます。

各識別子がどのようなものであるかを知るには、プログラム全体を (少なくとも、使用する時点まで) 解析する必要があるため、どの識別子がテンプレート識別子であるかを知る簡単な方法がないため、アルゴリズムを実装するのは困難です。これには、インクルード ファイルの検索と解析、およびソース コードの前処理が含まれます。

本当に正確にしたい場合は、C++ パーサーを使用する必要があります。これには、テンプレートのインスタンス化が含まれている可能性があるため (引用されたプライム チェックの例のように)、プロセスが非常に遅くなる可能性があることに注意してください。構文の色付けのようなことをしようとしているだけなら、おそらく近似でうまくいくでしょう。

于 2013-11-10T16:12:13.700 に答える
1

()括弧内はスキップする必要があります。
の前の識別子を見て、<それが型名かどうかを知る必要があります。

class/struct/unionこの 2 番目のものは問題のあるものです。すべてのソース コードをスキャンしてs とs のすべての名前を識別しtypedef、フォームの式 (この例では簡略化されています) に到達したときに、型に名前を付ける__w < __aかどうかを知る必要があります。 __w.
これが問題になる理由は、プリプロセッサ メタプログラミング (この主題に関する Boost のライブラリなど) に遭遇した場合、基本的に、これらを評価して作成された型名を確認できるプログラムの作成に行き詰まっているためです。
さらに、次のコードを考えてみましょう (これがどれほど難しいかを示すために、必要以上に複雑になっています)。

    template <template <class> class T>
    struct Base
    {
        template <class X>
        using type = T<X>;
    };

    template <>
    struct Base<std::numeric_limits>//Let's assume numeric_limits only has one template argument for this example
    {
        static const int type = 0;
    };

    template <class T, Base<T>::type < 0>
    struct OtherClass{};

この種の複雑さはtypenameキーワードの目的です。依存スコープであるため、名前が型なのかメンバー変数なのBase<T>かすぐにはわかりません。Base<T>::typeこのように、文法Base<T>::typeでは静的メンバー変数でtypename Base<T>::type あり、型である必要があります (型の種類はわかりませんが、テンプレート識別子リストの最後を見つけようとしているだけなので問題ありません)。上記の例に加えて、あまり知られていないキーワード
の使用法も処理する必要があります。template依存スコープなのでBase<T>、どのように解析しBase<T>::type<0>ますか? これは何typeであるかによって異なります。この場合の文法では、 がBase<T>::typeメンバー変数であり、 として解析される(Base<T>::type < 0) >必要がBase<T>::template type<0>あり、テンプレート式です。

キーワードtypenametemplateは、従属スコープを理解するまで理解するのが難しいですが、最終的には仕事が楽になります。そうしないと、テンプレート識別子リストがどこで終了するかという単純な質問は、完全なコンパイラーを作成しないとほとんど答えられなくなります (さらに、コンパイラーを作成するのははるかに困難です)。

依存スコープは別として、非依存スコープを処理できる必要があります。これは、 が型であるかどうかを知る必要があることを意味します。つまり、すべてのs をBase<numeric_limits>::typeスキャンし、基本クラスs とプライベート継承 +ステートメントの公開継承を解決します。struct/class/uniontypedeftypedefusing

コンパイルできるコードだけに制限すれば、仕事は扱いやすいままになります。

これがすべてであるとは約束しませんが、これでしばらく忙しくすることになるでしょう。

于 2013-11-10T16:12:54.160 に答える