43

検討:

template <typename T>
class Base
{
    public:
        static const bool ZEROFILL = true;
        static const bool NO_ZEROFILL = false;
}

template <typename T>
class Derived : public Base<T>
{
    public: 
        Derived( bool initZero = NO_ZEROFILL );    // NO_ZEROFILL is not visible
        ~Derived();
}

これを GCC g++ 3.4.4 (cygwin) でコンパイルできません。

これらをクラス テンプレートに変換する前は、それらは非ジェネリックであり、派生クラスは基本クラスの静的メンバーを参照できました。この可視性の喪失は C++ 仕様の要件ですか、それとも構文を変更する必要がありますか?

Base<T>の各インスタンス化には独自の静的メンバー " ZEROFILL" と " NO_ZEROFILL" がBase<float>::ZEROFILLあり、それは異なる変数であることは理解していますがBase<double>::ZEROFILL、あまり気にしません。定数は、コードを読みやすくするためにあります。マクロやグローバルよりも名前の競合に関して安全であるため、静的定数を使用したかったのです。

4

4 に答える 4

48

これが 2 フェーズ ルックアップです。

Base<T>::NO_ZEROFILL(マクロを除くすべてのキャップ識別子はブーです、ところで)はに依存する識別子ですT
コンパイラが最初にテンプレートを解析するとき、実際の型はTまだ置換されていないため、コンパイラは何が何であるかを「認識」していませんBase<T>。したがって、その中で定義されていると想定される識別子を知ることはできず (Tコンパイラが後でのみ参照するいくつかの s の特殊化がある可能性があります)、基本クラスで定義された識別子から基本クラスの修飾を省略することはできません。

Base<T>::NO_ZEROFILLそのため、 (または)を記述する必要がありますthis->NO_ZEROFILLNO_ZEROFILLこれは、 に依存する基本クラスの何かであり、T後でテンプレートがインスタンス化されたときにのみ検証できることをコンパイラに伝えます。したがって、コードを検証せずに受け入れます。
そのコードは、テンプレートが に実際のパラメーターを指定してインスタンス化されたときにのみ検証できますT

于 2009-08-06T16:12:16.243 に答える
29

発生した問題は、依存する基本クラスの名前検索ルールが原因です。14.6 / 8には:

テンプレート定義で使用される名前の宣言を検索する場合、依存しない名前には通常の検索ルール(3.4.1、3.4.2)が使用されます。テンプレートパラメータに依存する名前の検索は、実際のテンプレート引数がわかるまで延期されます(14.6.2)。

(これは実際には「2フェーズルックアップ」ではありません。その説明については、以下を参照してください。)

14.6 / 8についてのポイントは、コンパイラに関する限りNO_ZEROFILL、例では識別子であり、テンプレートパラメータに依存しないということです。したがって、3.4.1および3.4​​.2の通常のルールに従って検索されます。

この通常のルックアップは内部を検索しないBase<T>ため、NO_ZEROFILLは単に宣言されていない識別子です。14.6.2/3には次のものがあります。

クラステンプレートまたはクラステンプレートのメンバーの定義で、クラステンプレートの基本クラスがtemplate-parameterに依存している場合、クラスの定義の時点でも、非修飾名ルックアップ中に基本クラススコープは検査されません。テンプレートまたはメンバー、またはクラステンプレートまたはメンバーのインスタンス化中。

本質的に資格NO_ZEROFILLを得るとBase<T>::、それを非依存名から依存名に変更し、それを行うと、テンプレートがインスタンス化されるまで検索を遅らせます。

補足:2フェーズルックアップとは何ですか:

void bar (int);

template <typename T>
void foo (T const & t) {
  bar (t);
}


namespace NS
{
  struct A {};
  void bar (A const &);
}


int main ()
{
  NS::A a;
  foo (a);
}

上記の例は次のようにコンパイルされます。コンパイラーはの関数本体を解析し、依存引数(つまり、テンプレートパラメーターに依存する引数)を持つfoo呼び出しがあることを確認します。barこの時点で、コンパイラは3.4.1に従ってバーをルックアップし、これが「フェーズ1ルックアップ」です。ルックアップは関数を見つけ、void bar (int)それは後でまで依存呼び出しとともに保存されます。

テンプレートがインスタンス化されると(からの呼び出しの結果としてmain)、コンパイラは引数のスコープで追加のルックアップを実行します。これが「フェーズ2ルックアップ」です。この場合、結果としてvoid NS::bar(A const &)

コンパイラには2つのオーバーロードがbarあり、上記の場合はを呼び出して、どちらかを選択しますvoid NS::bar(A const &)

于 2009-08-10T10:16:23.870 に答える
1

vs 2008 では問題なくコンパイルできるようです。

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );
于 2009-08-06T16:11:03.950 に答える