2

Foo<T>私は派生したテンプレート クラスを持っていますFooDerived<T>。これは、テンプレート クラスのテンプレート引数です。Bar<T>つまり、最終的には のようなものになりBar< FooDerived<T> >ます。

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived : public Foo<T>
{
    public:
        double compute() { return 42.0; }
};

template <typename T>
class Bar : public T
{
    public:
        void f()
        {
            // This function could vary depending on U (and possibly T)
            std::cout << "Bar< T<U> > : " << this->compute() << std::endl;
        }
};

Barのテンプレート引数に基づいて特殊化しようとしていますFooDerived。たとえば、Bar< FooDerived<int> >andBar< FooDerived<float> >は異なる動作をします。

int main(void)
{
    Bar< FooDerived<int> > a;
    a.f();
    Bar< FooDerived<float> > b;
    b.f();
}

C++03 (または C++11) でこれを効率的に達成するにはどうすればよいですか? 効率的とは、無駄なコードの重複を避けたいという意味です(実際のプログラムには、この例よりも多くの型と関数が含まれます)。また、コードを使用する人は、コードを変更する必要なく専門化を追加できる必要があるため、どのような種類のswitchソリューションも適応されません。

boost::mplタイプをチェックするなど、boost::mpl::if_を含む SFINAE のようなソリューションを見てきましたがboost::is_same、私の目的を達成するものは何もないようでした。これは、その種のテンプレートの特殊化には適合しない可能性があると思います。error: redefinition of 'Bar'たとえば、次のようなことをしようとすると、コンパイラはそれを特殊化として認識していないように見えるため、常に終了します。

テンプレート クラス バー : public T、private boost::mpl::if_、int、boost::mpl::false_>

boost::mpl::if_をプライベート継承または追加のテンプレート引数として使用しても、特殊化は可能ではないようです。

では、このようなことを達成するための適切な方法は何ですか?

更新 1

すべてのケースを特殊化することは可能ですが、この例の背後に隠れている実際の問題は、実際にはもっと複雑です。ストレージタイプ(密または疎行列)である(関数、微分可能な関数、2回微分可能な関数など)から派生するCachedFunction<T> : public T場所があります。実装がストレージタイプと関数のタイプに依存する多数の関数が含まれています。したがって、メタプログラミングは、コードの重複を回避するための良い方法のように思えました。TFunction<U>UCachedFunction

更新 2

提供された回答に応じて:関連するすべてのケースで、これらの明示的なテンプレートの特殊化を回避しようとしています。から派生した 3 つまたは 4 つのクラスFooがあり、 には 2 つまたは 3 つの型があり、 の型と考慮される派生クラスに基づいて異なる処理が必要な 6 つまたは 7 つの関数が含まれFooていると想像してみてください。基本的に、 、およびごとに、次を実装する必要があります。BarFooijk

template<> void Bar<FooDerived_i<Type_j> >::f_k(){ ... }

したがって、他の「よりクリーンな」方法があるかどうかを確認しようとしています。

更新 3

を使用するboost::is_sameと、このようなことができますが、この解決策では、コードを変更せずに新しい型を処理することが難しくなります。

例を次に示します。

#include <iostream>
#include <boost/type_traits/is_same.hpp>

typedef int type1;
typedef float type2;

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived
{
    public:
        typedef T type_t;

        double compute() { return 42.0; }
};

template <class T>
class Bar : public T
{
public:
    void f()
    {
        // types have to be known...
        if (boost::is_same<typename T::type_t, type1>::value)
            std::cout << "Bar< T<type1> > : " << this->compute() << std::endl;
        else if (boost::is_same<typename T::type_t, type2>::value)
            std::cout << "Bar< T<type2> > : " << this->compute() << std::endl;
    }
};

int main(void)
{
    Bar< Foo<type1> > a;
    a.f();
    Bar< FooDerived<type2> > b;
    b.f();
}
4

3 に答える 3

0

あるコメントで述べたように、クラスを特殊化すると、実際の例では多くのコードが重複するため、避ける必要があります。

質問で述べているように、Foo (または FooDerived) のテンプレート パラメーターに関して「特化」したい場合は、Bar でテンプレート テンプレート パラメーターを定義することを検討する必要があります。たとえば Bar の場合、Bar のテンプレート引数にはテンプレート パラメーターがありません。

経由でこれを行うことができます

template <template<typename> class T, typename Arg> class Bar : public T<Arg> {
public:
    std::string name()
    {
        return "Bar<T<Arg>>";
    }
};

とはいえ、次のコードは、クラス全体を特殊化する必要なく特殊な動作を取得するために使用できるメソッドを示しています。

  1 #include <string>
  2 #include <iostream>
  3 
  4 namespace ExampleSpecializations{
  5         template <typename T>
  6         std::string name(){
  7                 return "generic";
  8         }       
  9         template <>
 10         std::string name<int>(){
 11                 return "int";
 12         }
 13 }
 14 
 15 template <typename Arg>
 16 struct Bar {
 17         std::string name()
 18         {
 19                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<Arg>() + std::string(">");
 20                 return ret; 
 21         }       
 22 };      
 23 
 24 int main()
 25 {
 26         Bar<int> a;
 27         std::cout << a.name() << std::endl;
 28         Bar<char> b; 
 29         std::cout << b.name() << std::endl;
 30 }       
 31 

関連するクラスの内部にアクセスする必要のないメンバー関数を追加していないため、外部関数 (4 行目から 13 行目まで) の使用も適切なカプセル化を提供することに注意してください。

  1 #include <string>
  2 #include <iostream>
  3 
  4 template <typename T>
  5 struct Foo1{
  6 };              
  7                         
  8 template <typename T>   
  9 struct Foo2{
 10 };              
 11 
 12 namespace ExampleSpecializations{
 13         template <typename T>
 14                 std::string name(const T& ){
 15                         return "generic"; 
 16                 }       
 17         template <>
 18                 std::string name(const int&){
 19                         return "int";
 20                 }       
 21         template <typename T>
 22                 std::string name(const Foo1<T>& ){
 23                         std::string  ret = "Foo1<" + ExampleSpecializations::name<T>(T()) + ">";
 24                         return ret;  
 25                 }       
 26         template <typename T>
 27                 std::string name(const Foo2<T>& ){
 28                         std::string  ret = "Foo2<" + ExampleSpecializations::name<T>(T()) + ">";
 29                         return ret;  
 30                 }       
 31 }               
 32 
 33 template <typename Arg>
 34 struct Bar {
 35         std::string name()
 36         {
 37                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<>(Arg()) + std::string(">");
 38                 return ret; 
 39         }       
 40 };      
 41 
 42 int main()
 43 {
 44         Bar<int> a;
 45         std::cout << a.name() << std::endl;
 46         Bar<char> b; 
 47         std::cout << b.name() << std::endl;
 48         
 49         Foo1<int> f1i;
 50         std::cout << ExampleSpecializations::name(f1i) << std::endl;
 51         
 52         Foo2<int> f2i;
 53         std::cout << ExampleSpecializations::name(f2i) << std::endl;
 54         
 55         Bar<Foo2<int> > bf2i;
 56         std::cout << bf2i.name() << std::endl;
 57 }

ここでコード を試す 言うまでもなく、Bar::name も非メンバー関数にすることをお勧めします。

最後に、「一般的な」動作を提供したくない場合は、次のようにデフォルトの実装を提供しないだけです。

        template <typename T>
            std::string name(const T& );

これにより、「name」メソッドが Bar で呼び出されるため、前のコードはエラーを発行します。このエラーは、47 行目をコメントアウトするか、char 特殊化を実装することで解決されます。

于 2013-06-28T07:15:34.710 に答える
0

これはどう?

template<>
struct Bar< FooDerived<int> >
{
   // int spezialisation here
};

template<>
struct Bar< FooDerived<float> >
{
   // float spezialisation here
};

動作は異なるがシグネチャが異なるいくつかの関数のみを特殊化するには、次の方法があります。

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
}

アップデート:

2回目の更新のメモとして、この場合、専門化が異なる動作を実装する唯一のクリーンな方法だと思います。ただし、テンプレートをより細かく使用すれば、もちろん多くのコードを再利用できます。

template<class T>
std::string name_impl(T& t) {
  // generic implementationm
}

std::string name_impl(Bar<FooDerived<float>& t) {
  // implementation
}

template<class T>
std::string Bar<T>::name() {
  // something befor
  return name_impl(*this);
}

したがって、テンプレートの専門化よりも簡単な関数のオーバーロードを介して、コンパイル時のポリモーフィズム (継承、enable_if など) に切り替えることができます。これはコンパイル時のテンプレート パターンです。

編集: 3回目の更新ではないので、ここで私の回答を読むことをお勧めします。あなたの実装では、メソッドで問題が発生する可能性が最も高いでしょうf

于 2013-06-28T06:55:42.050 に答える
0

(注、これは質問の最初のバージョンに回答し ます。削除する必要があると思われる場合は、コメントでお知らせください)

できるよ:

template <typename T>
class Bar : public T
{
    public:
        std::string name();
};

template<>
std::string Bar<FooDerived<int> >::name() {
  return std::string("int");
  }

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
  }

ここでテスト: http://coliru.stacked-crooked.com/view?id=9054d3356f438b31b1adbb595620d838-ad7854d9cfd7979d567ca413f0830b65

貴重な提案をしてくれたjogojapanに感謝します!

于 2013-06-28T06:52:43.840 に答える