C ++编译时多态性

时间:2012-01-22 14:47:55

标签: c++ templates polymorphism static-methods

有两个不相关的结构A和B

template <typename T>
struct A {};

template <typename T>
struct B {};

一个枚举类型

typedef enum  { ma, mb} M;

和包含函数模板的C类

class C
{
public: 
    template <typename T>
    static void f1 ( A <T> &a) {}

    template <typename T>
    static void f2 ( B <T> &b) {}

    template <typename U>
    static void algo (U &u, M m)
    {
        /*Long algorithm here                     
        ....
        */
        if ( m == ma) f1(u);
        else f2(u);
    }
};

静态方法算法包含一些算法,这很难......它将一些值和结果修改为结构A或B.

我想使用对象A或B运行静态方法算法,具体取决于M值。但是如何对我的编译器说: - )

int main()
{
A <double> a;
C::algo (a, ma); //Error

}

Error   1   error C2784: 'void C::f1(A<T>)' : could not deduce template argument for 'A<T>' from 'B<T>

A]我在考虑指向函数的指针,但它们不能用于函数模板。

B]也许编译多态可以帮助

template <typename U, M m>
static void algo (U &u, M <m> ) { ...}  //Common for ma

template <typename U, M m>
static void algo (U &u, M <mb> ) { ...} //Spec. for mb

但是这个解决方案有一个大问题:两个实现都应该不必要地包含几乎相同的代码(为什么要编写两次算法?)。

所以我需要一个函数 algo()处理两种类型的参数A和B.是否有更舒适的解决方案?

4 个答案:

答案 0 :(得分:3)

您似乎正在使用枚举来传达用户的类型信息。我建议你不要。

在最简单的情况下,如果f1f2重命名为f,那么您可以完全删除if并调用它。编译器会为您调用适当的重载。

如果您不能或不想重命名功能模板,那么您可以编写一个将为您调度的辅助模板(未定义的基本类模板,发送的AB的特殊化到适当的静态函数)

如果枚举用于其他东西(编译器无法为你解析)​​,你仍然可以传递它并重写助手来调用枚举而不是参数的类型,你将不得不重写将枚举值作为编译时常量的代码(最简单:将其作为模板参数传递给algo)。在这种情况下,如果需要,您可以编写函数特化而不是类,因为它们是完全特化。但请注意,如果您可以避免必须通过它,您将删除整个错误系列:传递错误的枚举值。

// Remove the enum and rename the functions to be overloads:
//
struct C {  // If everything is static, you might want to consider using a
            // namespace rather than a class to bind the functions together...
            // it will make life easier

   template <typename T>
   static void f( A<T> & ) { /* implement A version */ }

   template <typename T>
   static void f( B<T> & ) { /* implement B version */ }

   template <typename T> // This T is either A<U> or B<U> for a given type U
   static void algo( T & arg ) {
      // common code
      f( arg ); // compiler will pick up the appropriate template from above
   } 
};

对于其他替代方案,如果封闭范围是命名空间,则更容易,但想法是相同的(可能需要更加努力地对抗语法:

template <typename T>
struct dispatcher;

template <typename T>
struct dispatcher< A<T> > {
   static void f( A<T>& arg ) {
      C::f1( arg );
   }
};
template <typename T>
struct dispatcher< B<T> > {
   static void f( B<T>& arg ) {
      C::f2( arg );
   }
};

template <typename T>
void C::algo( T & arg ) {
   // common code
   dispatcher<T>::f( arg );
}

同样,让它与一个类一起使用可能有点棘手,因为它可能需要一些前向声明,而且我手头没有编译器,但草图应该引导你朝着正确的方向前进。

答案 1 :(得分:2)

正常函数重载就足够了:

template <typename T> 
static void f1 ( A <T> &a) {} 

template <typename T> 
static void f2 ( B <T> &b) {} 

template <typename T> 
static void algo (A<T>& u) {
    f1(u);
} 

template <typename T> 
static void algo (B<T>& u) {
    f2(u);
} 

然后:

A<int> a;
Foo::algo(a);

虽然目前尚不清楚从这样的安排中获得什么。

答案 2 :(得分:0)

如果你真的需要在一个函数中执行此操作,可以使用type_traits:

 template<typename T, T Val>
 struct value_type { static const T Value = Val; };

 struct true_type   : public value_type<bool, true>{};
 struct false_type  : public value_type<bool, false>{};


 template<class T>
 struct isClassA : public false_type{};

 template<>
 struct isClassA<A> : public true_type{};


 template < typename T >
 void Algo( T& rcT )
 {
    if ( true == isClassA<T>::Value )
    {
        // Class A algorithm
    }
    else
    {
        // Other algorithm
    }
 };

答案 3 :(得分:0)

m参数的值在运行时之前是未知的,因此编译器在专门化函数时必须为if (m == ma)else分支生成代码。 然后它抱怨,因为如果你恰好打电话给C::algo(a,mb)或类似的话,它无法理解他应该怎么做。

正如Jon建议的那样,重载应该可以修复你的情况,尝试使用这段代码:

template<typename U>
static void f12(A<U>&u) { f1(u); }

template<typename U>
static void f12(B<U>&u) { f2(u); }

template<typename U>
static void algo(U& u, M m)
{
    /* long algorithm here
      ...
    */
    //use overloading to switch over U type instead of M value
    f12(u);
}

此外,可以使用带有模板函数的函数指针,只要指定模板参数:

template<typename U>
static void algo(U& u, M m, void(*)(U&) func)
{
    /* ... */
    (*func)(u);
}

int main()
{
    A <double> a;
    C::algo (a, ma, &C::f1<double> );
}