1

テンプレート パラメーターに入れ子になった type/typedef XYZ があるかどうかを確認したいとします。

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;
   template <class U>
   static yes f(typename U::XYZ*);
   template <class /*U*/>
   static no  f(...);
   enum {value = sizeof(f<T>(0))==sizeof(yes)};
};

期待どおり、正常に動作します。

これを考慮してください:

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;

   static yes f(typename T::XYZ*);
   static no  f(...);
   enum {value = sizeof(f(0))==sizeof(yes)};
};

hasXYZ<int>コンパイル時エラーが発生するようになりました。OK、f はテンプレート関数ではありません。しかし一方で、hasXYZが int を介してインスタンス化されると、コンパイラは候補リストからhasXYZ<int>::value簡単に除外できます。f(int::XYZ*)クラス テンプレートのメンバー関数宣言のインスタンス化に失敗すると、クラスのインスタンス化全体が失敗する理由がわかりません。何か案は?

編集:私の質問は:なぜメンバー関数宣言はすべて整形式でなければならないのですか? コンパイラは使用時にのみメソッドをインスタンス化するため、正しい宣言が必要なのはなぜですか。この機能の使用例として、上記の例 2 を検討してください。

4

3 に答える 3

4

SFINAE は、関数オーバーロード解決の候補セットを作成する場合にのみ使用されます。最初の例では、オーバーロードされた f() 関数を呼び出していますが、最初の例は SFINAE のおかげで除外されています。

2 番目の例では、hasXZY をインスタンス化するときに、そのすべてのメンバーを適切に定義する必要があり、テンプレート パラメーターの置換が失敗してはなりません。int::XYZ の場合です。

メンバーは、置換の失敗によってクラスから除外されません。

于 2010-10-22T19:42:19.180 に答える
3

私はC++言語の弁護士ではありませんが、これを試してみます。

2番目の例では、メンバー関数は、に対してインスタンス化されるとテンプレート関数ではなくなるため、明確に定義する必要があります。これを納得させるには、「手作業」の代わりに使用します。hasXZYintT

struct hasXYZ
{
    typedef int                   T;
    typedef char                  no;
    typedef struct { char x[2]; } yes;

    static yes f(T::XYZ*);
    static no  f(...);
    enum {value = sizeof(f(0))==sizeof(yes)};
};

int main()
{
    std::cout << hasXYZ::value << "\n";
}

そして、これがコンパイルに失敗し、以前と同じコンパイラエラーが発生することを確認します(少なくともGCCでは)。

foo.cc:9: error: ‘T’ is not a class or namespace

対照的に、最初の例は、手動でインスタンス化した後、期待どおりにコンパイルおよび動作します。fメンバーはまだテンプレート化されていますU

于 2010-10-22T20:57:05.423 に答える
2

編集:私の質問は:なぜメンバー関数宣言はすべて整形式でなければならないのですか? コンパイラは使用時にのみメソッドをインスタンス化するため、正しい宣言が必要なのはなぜですか。この機能の使用例として、上記の例 2 を検討してください。

クラス テンプレートの特殊化を暗黙的にインスタンス化する場合、コンパイラは宣言に関する基本的な情報を知る必要があるため、そのメンバーの完全な宣言子を検査する必要があります。これは、クラス テンプレートの特殊化のサイズに寄与する可能性があります。

宣言部分を調べて、データメンバーを宣言していることがわかった場合sizeof、クラスの値は異なる値になる可能性があります。代わりに関数ポインターを宣言した場合、これが当てはまります

yes (*f)(typename T::XYZ*);

C++ 言語は、宣言全体が解析されて初めて宣言の型がわかるように定義されています。

そこに static を入れたと主張することができます。したがって、この場合、これはそのサイズを計算するために必要ありません。しかし、name-lookup では、名前が何を参照しているか、名前fが宣言されているかどうかを知る必要があります。コンパイラはの定義をインスタンス化するのではなく、宣言の非定義部分のみをインスタンス化して、その型を取得し、名前検索のためにその名前をクラス型に追加します。名前の宣言の遅延インスタンス化をサポートすると、それが機能する可能性がある特定のケースで、C++ コンパイラと C++ 仕様の実装がさらに複雑になり、同等の利点が得られないと思います。hasXZY<T>::fhasXYZ::f

最後に、それを呼び出そうとする例では、コンパイラname を検索する必要があるため、宣言をインスタンス化する必要があります。fこのために、その宣言が関数であるか他のものであるかを知る必要があります。したがって、理論的には、宣言をインスタンス化せずに例が機能する方法を実際に見ることはできません。いずれにせよ、これらは関数の定義をインスタンス化しないことに注意してください。

于 2010-10-23T11:48:52.483 に答える