2

私が以前に尋ねた質問の当然の帰結として、テンプレートの名前のタイプ (その名前の「カテゴリ」) のタイプが 2 フェーズ ルックアップの最初のフェーズで設定されているのに、カテゴリ自体が可能である理由に興味があります。テンプレートパラメータにも依存します。この行動の本当の利益は何ですか?

少し明確にします - 私は、2 フェーズのルックアップがどのように機能するかについてかなり理解していると思います。私が理解しようとしているのは、トークンのカテゴリがフェーズ 1 で決定的に決定される理由です。これは、依存型が決定されるとき (フェーズ 2) とは異なります。私の主張は、難しい構文を単純化して、コードを書きやすく読みやすくすることには非常に大きなメリットがあるということです。そのため、カテゴリ評価をフェーズ 1 に制限する説得力のある理由が何であるかに興味があります。テンプレートのインスタンス化前のテンプレートの検証/エラー メッセージを改善するためですか、それとも速度をわずかに向上させるためですか? それとも、フェーズ 2 のカテゴリ評価を実行不可能にするテンプレートの基本的な属性がありますか?

4

2 に答える 2

2

問題は 2 つある可能性があります。そもそもなぜ 2 フェーズ ルックアップが必要なのか、2 フェーズ ルックアップがあるとすれば、トークンの解釈が最初のフェーズで修正されるのはなぜなのかということです。最初の質問は答えにくい質問です。これは言語の設計上の決定であり、長所と短所があり、立場に応じてどちらかが重要になるためです。

あなたが興味を持っている2番目の部分は、実際にはもっと単純です。なぜ、2 フェーズ ルックアップを使用する C++ 言語では、トークンの意味は最初のフェーズで固定され、2 番目のフェーズで解釈されるままにすることはできません。その理由は、C++ には文脈文法があり、トークンの解釈は文脈に大きく依存するためです。最初のフェーズでトークンの意味を固定しなければ、最初にどの名前を検索する必要があるかさえわかりません。

リテラル 5 が定数式に置き換えられている、元のコードのわずかに変更されたバージョンを考えてみてください。また、前回はあなたを苦しめたtemplateorキーワードを提供する必要がなかったと仮定します。typename

const int b = 5;
template<typename T>
struct Derived : public Base<T> {
    void Foo() { 
       Base<T>::Bar<false>(b);   // [1]
       std::cout << b;           // [2]
    }
};

[1] の可能な意味は何ですか (C++ では と を追加することによって決定されるという事実を無視しtypenametemplate)

  1. Barbool単一のテンプレート引数と整数を引数として取る静的テンプレート関数です。b定数 5 を参照する非依存の名前です。*

  2. Barboolテンプレート引数として1 つを受け取る、ネストされたテンプレート タイプです。b関数内で定義され、Derived<T>::Foo使用されていない型のインスタンスです。

  3. Barを取り、結果として整数と比較できる型のオブジェクトを生成Xする比較がある型の静的メンバー変数です。operator<boolUoperator>

問題は、テンプレートの引数が代入される前に (つまり、最初のフェーズで)、名前の解決をどのように進めるかです。ケース 1. または 3. の場合は、b検索する必要があり、結果を式に代入できます。最初のケースでは元のコードを生成します: Base<T>::template Bar<false>(5)、後者のケースでは を生成しoperator>( operator<( Base<T>::Bar,false ), 5 )ます。3 番目のケース (2.) では、最初のフェーズの後のコードは、元のコードとまったく同じになりますBase<T>::Bar<false> b;(余分な を削除します())。

2 行目の意味 [2] は、最初の行をどのように解釈したかによって異なります [1]。2. の場合は への呼び出しを表しoperator<<( std::cout, Base<T>::Bar<false> & )、他の 2 つの場合は を表しoperator<<( std::cout, 5 )ます。2.b内のケース名Derived<T>::Fooが依存しているように、2 番目の引数の型を超えて影響が及ぶため、最初のフェーズでは解決できず、2 番目のフェーズに延期されます (2 番目の引数を追加することで検索にも影響します)の名前空間Baseとインスタンス化する型Tを Argument Dependent Lookup に渡します)。

例が示すように、トークンの解釈は名前の意味に影響を与え、それはコードの残りの部分の意味、どの名前が依存しているかどうか、したがって最初の実行中に他に何を調べる必要があるかどうかに影響します。段階。同時に、コンパイラは最初のパスでチェックを実行し、2 番目のパスでトークンを再解釈できる場合、最初のパスでのチェックとルックアップの結果は役に立たなくなります (最初のパスでそれを想像してください)。 passbは、第 2 フェーズでケース 2 にあることを確認するためだけに置き換えられ5ました!)、第 2 フェーズですべてをチェックする必要があります。

2 フェーズ ルックアップの存在は、解釈されるトークンと、最初のフェーズで選択された意味に依存します。代替手段は、VS のようにシングル パス ルックアップです。


*ここではケースを単純化しています。2 フェーズ ルックアップを実装していない Visual Studio コンパイラでは、現在インスタンス化されている型のbメンバーになることもできます(つまり、依存名になる可能性があります)。Base<T>T

于 2012-10-16T02:15:18.127 に答える
0

C++ の利点の多くは、厳密にチェックされた言語であることです。プログラムの意図を可能な限り明確に表現すると、コンパイラはその意図に違反しているかどうかを通知します。

Base<T>::Bar<false>(b);あなたが(Dribeasの例から)書いて、あなたが望む特定の解釈を持っていないとは想像できません。解釈をコンパイラ ( Base<T>::typename Bar<false>(b);) に伝えることで、誰かがメンバー関数テンプレートの代わりにstaticメンバーBarまたはネストされたテンプレート型を持つ型を提供した場合に、意味のあるエラーを生成できます。

他の言語は、静的分析よりも簡潔さを強調するように設計されています。たとえば、多くの動的言語には、多数の "do what I mean" ルールがあります。これは、コンパイラが無意味なコードをエラーなしで予測不可能なものに変えるときに楽しいことを引き起こします。(適切な例: Perl。テキスト操作には Perl が気に入っていますが、DWIM は面倒です。ほとんどすべてが実行時エラーであり、静的チェックはほとんどありません)

于 2012-10-16T05:52:00.653 に答える