简体   繁体   English

C ++编译时多态

[英]C++ Compile-time polymorphism

There two unrelated structures A and B 有两个不相关的结构A和B

template <typename T>
struct A {};

template <typename T>
struct B {};

one enum type 一种枚举类型

typedef enum  { ma, mb} M;

and class C containing function templates 和包含功能模板的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);
    }
};

Static method algo contains some algorithm, that is quite difficult... It modified some values and results into structure A or B. 静态方法算法包含一些相当困难的算法...它修改了一些值并将结果转换为结构A或B。

I would like to run static method algo with objects A or B depending on M value. 我想根据对象M的值使用对象A或B运行静态方法算法。 But how to say it to my compiler :-) 但是怎么说对我的编译器:-)

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] I was thinking about pointer to function, but they are not usable with function templates. A]我在考虑函数指针,但是它们不能与函数模板一起使用。

B] Maybe a compile polymorphism could help 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

But this solution has one big problem: Both implementations should unnecessarily include almost the same code (why to write the algorithm twice?). 但是此解决方案有一个大问题:两个实现都不必要地包含几乎相同的代码(为什么要两次编写算法?)。

So I need one function algo() processing both types of arguments A and B. Is there any more comfortable solution? 因此,我需要一个函数 algo()处理参数A和B的两种类型。还有没有更舒适的解决方案?

It seems that you are using the enum to convey type information from the user. 似乎您正在使用枚举来传达用户的类型信息。 I would suggest that you don't. 我建议你不要。

In the simplest case if f1 and f2 are renamed f , then you can remove the if altogether and just call it. 在最简单的情况下,如果将f1f2重命名为f ,则可以完全删除if并直接调用它。 The compiler will call the appropriate overload for you. 编译器将为您调用适当的重载。

If you cannot or don't want to rename the function templates, then you can write a helper template that will dispatch for you (basic class template undefined, specialisations for A and B that dispatch to the appropriate static function) 如果您不能或不想重命名功能模板,则可以编写一个将为您分派的帮助程序模板(未定义基本类模板, AB专业化将分派到适当的静态函数)

If the enum is used for something else (that the compiler cannot resolve for you), you can still pass it around and rewrite the helper to dispatch on the enum rather than the type of the argument and you will have to rewrite the code to have the enum value as a compile time constant (simplest: pass it as template argument to algo ). 如果枚举用于其他用途(编译器无法为您解决),您仍然可以将其传递给它,并重写帮助程序以在枚举上分派,而不是参数的类型,并且必须重写代码以具有枚举值作为编译时间常数(最简单:将其作为模板参数传递给algo )。 In this case ou can write function specialisations instead of classes if you want, as they would be full specialisations. 在这种情况下,您可以根据需要编写函数专业化而非类,因为它们将是完全专业化。 But note that if you can avoid having to pass it you will remove a whole family of errors: passing the wrong enum value. 但是请注意,如果可以避免通过它,则将消除整个错误系列:传递错误的枚举值。

// 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
   } 
};

For the other alternatives, it is easier if the enclosing scope is a namespace, but the idea would be the same (just might need to fight the syntax a bit harder: 对于其他替代方法,如果封闭范围是名称空间,则会更容易,但是想法是相同的(只是可能需要更严格地使用语法:

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 );
}

Again, getting this to work with a class might be a bit trickier as it will probably need a couple of forward declarations, and I don't have a compiler at hand, but the sketch should lead you in the right direction. 再说一次,让它与一个类一起使用可能会有些棘手,因为它可能需要几个前向声明,而且我手头没有编译器,但是草图应该可以引导您正确的方向。

Normal function overloading is sufficient: 正常的函数重载就足够了:

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);
} 

And then: 接着:

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

Although it's not clear what you stand to gain from such an arrangement. 尽管目前尚不清楚您将从这种安排中获得什么收益。

If you realy need to do that in one function, you can use typetraits: 如果确实需要在一个函数中执行此操作,则可以使用typetraits:

 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
    }
 };

the value of m parameter is unknown until runtime, so the compiler has to generate code for both if (m == ma) and else branches when it specialize the function. m参数的值直到运行时才是未知的,因此当编译器专门化该函数时,编译器必须同时为if (m == ma)else分支生成代码。 It then complains since it can't understand what he should do if you happen to call C::algo(a,mb) or similar. 然后它抱怨,因为如果您碰巧调用C::algo(a,mb)或类似名称C::algo(a,mb)它将无法理解他应该怎么做。

As Jon suggested, overloading should fix your case, try using this code: 正如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);
}

Also you can use function pointers with template functions, as long as you specify the template parameters: 还可以将函数指针与模板函数一起使用,只要您指定模板参数即可:

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> );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM