9

こんにちは、明示的な特殊化を持つテンプレート化されたクラスの正しいバージョンを選択する際に問題があります。特殊化に使用されるクラスの派生クラスを使用して、特殊化を選択したいと考えています。シナリオは次のとおりです。

#include <stdio.h>

class A
{};

class B: public A
{};

template<typename T>
class Foo
{
public:
   int FooBar(void) { return 10; }
};

// Explicit specialization for A
template<> int Foo< A >::FooBar( void ) { return 20; }

void main( void)
{
   Foo<B> fooB;

   // This prints out 10 instead of wanted 20 ie compiler selects the general version
   printf("%d", fooB.FooBar() );
}

私がコメントで言っているように、B は A から派生したものですが、代わりに 10 が出力されるため、20 が出力されることを確認したいと思います。派生クラスごとに特殊化を記述することに頼らずに、特殊化を呼び出すにはどうすればよいですか (私の実際のシナリオに多く派生型があります)。

4

6 に答える 6

7

---編集:新しい回答元のアプローチをより保守しやすくしましょう。すべての重要な選択肢は、Fooの定義にあります。メンテナンスが簡単なはずです。

#include <boost/mpl/if.hpp>
#include  <boost/type_traits/is_base_of.hpp>
#include <iostream>

class A
{};

class B: public A
{};

class C{};
class D : public C{};
class E{};

struct DefaultMethod
{
    static int fooBar() { return 10; }
};
struct Method1
{
    static int fooBar() { return 20; }
};
struct Method2
{
    static int fooBar() { return 30; }
};

template<typename T, typename BaseClass, typename Choice1, typename OtherChoice>
struct IfDerivesFrom :
    boost::mpl::if_<
        typename boost::is_base_of<BaseClass, T>::type,
        Choice1,
        OtherChoice>::type
{
};

template<typename T>
struct Foo :
    IfDerivesFrom<T, A,
      Method1,
      IfDerivesFrom<T, C,
          Method2,
          DefaultMethod>
      >
{
};

int main()
{
    std::cout << Foo<A>::fooBar() << std::endl;
    std::cout << Foo<B>::fooBar() << std::endl;
    std::cout << Foo<C>::fooBar() << std::endl;
    std::cout << Foo<D>::fooBar() << std::endl;
    std::cout << Foo<E>::fooBar() << std::endl;

    return 0;
}

---元の回答ブーストを使用できる場合は、次のようなことを行うことができます。

#include  <boost/type_traits/is_base_of.hpp>

template<bool b>
class FooHelper
{
    int FooBar();
};
template<> FooHelper<true>::FooBar(){ return 20;}
template<> FooHelper<false>::FooBar(){ return 10;}

template<typename T>
class Foo
{
public:
   int FooBar(void) { return FooHelper<boost::is_base_of<A, T>::type::value>(); }
};
于 2010-01-22T17:07:03.943 に答える
5

より一般的には、これはテンプレートと継承全般に関する長年の問題です。

問題は、テンプレートが正確な型で機能し、継承係数を考慮しないことです。2 つの概念は多少直交しているため、一方と他方を混在させようとすると、エラーが発生しやすくなります。

メソッドでチェックアウトすることもできます:

template <class T>
int fooBar(T) { return 10; }

int fooBar(A) { return 20; }

B b;
fooBar(b); // this returns 10, because fooBar<T> is a better match (no conversion)

enable_ifさて、あなたの問題についてですが、とトリックを使用して与えられたさまざまな解決策に感謝しますが、is_base_ofそれらは実用的ではないとして破棄します。専門化のポイントは、作成者がFoo必要に応じて自分のクラスをどのように専門化するかを知る必要がなく、単に簡単にするためであるということです。そうでなければ、1ダースの専門化が必要な場合、非常に奇妙なFooクラスになってしまうことは間違いありません.

STL はすでに同様の問題を扱っています。一般に受け入れられているイディオムは、特性クラスを提供することです。デフォルトの特性クラスは、すべての人に適切なソリューションを提供しますが、特性クラスを特殊化してニーズに対応することもできます。

コンセプトを使用する方法があるはずだと思います (つまり、T が T::fooBar() を定義している場合はそれを使用し、そうでない場合はデフォルト バージョンを使用します...)、特定のメソッドのオーバーロードについては、これは必要ありません。

namespace detail { int fooBar(...) { return 10; } }

template <class T>
class Foo
{
public:
  static int FooBar() { T* t(0); return ::detail::fooBar(t); }
};

そして今、A の派生クラスに特化するには:

namespace detail { int fooBar(A*) { return 20; } }

それはどのように機能しますか?オーバーロードを考慮する場合、省略記号は考慮される最後のメソッドであるため、前に修飾されたものはすべて実行されるため、デフォルトの動作に最適です。

いくつかの考慮事項:

  • 名前空間: 識別子fooBarが使用される可能性があるかどうかに応じて、独自の (またはFooクラス専用の) 名前空間に分離することを好む場合があります。そうでない場合は、非修飾呼び出しを行い、ユーザーに名前空間で定義させます彼女のクラス。

  • このトリックは、継承とメソッド呼び出しでのみ機能します。特別な typedef を導入したい場合は機能しません。

  • 実際の型のように、より多くのテンプレートを実際のメソッドに渡すことができます

これはテンプレート関数の例です

namespace detail { template <class T> int fooBar(...) { return 10; } }

template <class T>
int Foo<T>::FooBar() { T* t(0); return ::detail::fooBar<T>(t); }

namespace detail {
  template <class T>
  int fooBar(A*)
  {
    return T::FooBar();
  }
}

そして、ここで何が起こるか:

struct None {};
struct A { static int FooBar() { return 20; } };
struct B: A {};
struct C: A { static int FooBar() { return 30; } };

int main(int argc, char* argv[])
{
  std::cout << Foo<None>::FooBar()  // prints 10
     << " " << Foo<A>::FooBar()     // prints 20
     << " " << Foo<B>::FooBar()     // prints 20
     << " " << Foo<C>::FooBar()     // prints 30
     << std::endl;
}
于 2010-01-22T17:58:28.703 に答える
1

これが解決策ですが、それは特に良いことではありません:

template<typename T>
class Foo
{
public:
  int FooBar(typename disable_if<boost::is_base_of<A,T> >::type* dummy = 0) { return 10; }
  int FooBar(typename enable_if<boost::is_base_of<A,T> >::type* dummy = 0) { return 20; }
};
于 2010-01-22T17:17:04.940 に答える
1

最初の (マイナーな) ポイント: あなたのタイトルは間違っています。これは明示的な特殊化であり、部分的な特殊化ではありません。部分的な特殊化を行うには、少なくとも 1 つのテンプレート パラメーターを指定する必要がありますが、他の少なくとも 1 つのパラメーターは未指定のままにしておきます。

template <class T, class U>
demo { };

template <class T>
demo<int> {};  // use in the case of demo<XXX, int> 

あなたのコードを見ると、それがまったくコンパイルされていることに少し驚いています。特殊な関数を強制的に呼び出す方法があるかどうかはわかりません。通常、クラス全体を特殊化します。

template<typename T>
class Foo
{
public:
   int FooBar(void) { return 10; }
};

template<>
class Foo<A> {
public:
    int FooBar() { return 20; }
};

この場合、それは実際には何の役にも立ちません。派生オブジェクトをベース オブジェクトに暗黙的に変換できますが、それでも変換です。一方、特殊化されていないバージョンのテンプレートは、変換なしで使用できます。使用するバージョンを選択する際、コンパイラは、変換なしでインスタンス化できるバージョンを、暗黙的なテンプレートを必要とするバージョンよりも適切な選択肢として扱います。変換。

于 2010-01-22T16:59:58.360 に答える
0

基本的に、派生クラスでテンプレートの特殊化トリガーが必要です。

実際のクラスを特殊化する必要がなく、関数だけが必要な場合は、次のようにできます。

int foo::foobar(A &someA);

その後、実際にクラスを特殊化する必要がある場合は、インターフェイスとプライベート クラスのデータ パターンを確認する必要があると思います。多かれ少なかれ、インターフェイスはオブジェクトをテンプレートの特殊化で認識される型に「縮小」してから呼び出します。アラ

int foo::foobar(A &someA)
{ return fooImpl<A>::foobar(someA); }

しかし、それは一般的なケースをサポートしていないため、実際にはあなたの質問には答えていないと思います. 私はあなたが持つことができると思います:

template<class T>
class foo
{
public: 
    int foobar(T &t);
    int foobar(A &a);
}

foo<A>::foobar(someA);
foo<F>::foobar(someA);
foo<not B>::foobar(someB); //Should trigger foobar(A &a), right?

これにより、一般的なケースを提供しながら、B が A から派生したものとして認識できるようになります。おもう; 私はこれをテストしていません。

それは最もきれいではありませんが、実際のクラスを専門化している場合は、さまざまな foobar(A &a) のような関数を許可または許可するかどうかを指定できるため、アクセス制御のようなものをいくつか楽しいものにする機会があると思います。さまざまな継承ツリーでの使用を拒否します。上記の例では;

foo<C>::foobar(someF); //doesn't exist!
于 2010-01-22T18:42:08.557 に答える
0

正確な型に特化する必要があります。たとえばFoo<A> fooA; fooA.FooBar();20. または@Benoît が示すようにboost.type_traitsを使用します。

于 2010-01-22T16:59:44.440 に答える