C++编译时多态性

C++编译时多态性,c++,templates,polymorphism,static-methods,C++,Templates,Polymorphism,Static Methods,有两个不相关的结构A和B template <typename T> struct A {}; template <typename T> struct B {}; 和包含函数模板的C类 class C { public: template <typename T> static void f1 ( A <T> &a) {} template <typename T> static voi

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

template <typename T>
struct A {};

template <typename T>
struct B {};
和包含函数模板的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);
    }
};
静态方法algo包含一些算法,这是相当困难的。。。它将一些值和结果修改为结构A或B

我想用对象A或B运行静态方法algo,具体取决于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
但是这个解决方案有一个大问题:两个实现都应该不必要地包含几乎相同的代码为什么要编写两次算法


因此,我需要一个函数算法来处理两种类型的参数A和B。有没有更合适的解决方案?

正常函数重载就足够了:

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

虽然还不清楚这样的安排会给您带来什么好处。

正常的函数重载就足够了:

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

虽然还不清楚这样的安排会给你带来什么好处。

如果你真的需要在一个函数中实现这一点,你可以使用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
    }
 };

如果您确实需要在一个函数中执行此操作,则可以使用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
    }
 };

您似乎正在使用enum从用户传递类型信息。我建议你不要

在最简单的情况下,如果f1和f2被重命名为f,那么您可以将if一起删除,只需调用它。编译器将为您调用适当的重载

如果您不能或不想重命名函数模板,那么您可以编写一个助手模板,为您分派未定义的基本类模板,为a和B分派到相应的静态函数

如果枚举用于编译器无法为您解析的其他内容,您仍然可以将其传递并重写助手以在枚举上分派,而不是参数的类型,并且您必须重写代码以将枚举值作为编译时常量:将其作为模板参数传递给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 );
}

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

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

在最简单的情况下,如果f1和f2被重命名为f,那么您可以将if一起删除,只需调用它。编译器将为您调用适当的重载

如果您不能或不想重命名函数模板,那么您可以编写一个助手模板,为您分派未定义的基本类模板,为a和B分派到相应的静态函数

如果枚举用于编译器无法为您解析的其他内容,您仍然可以将其传递并重写助手以在枚举上分派,而不是参数的类型,并且您必须重写代码以将枚举值作为编译时常量:将其作为模板参数传递给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 );
}
同样,让它与类一起工作可能有点棘手,因为它可能需要两个正向声明,而且我手头没有编译器,但草图应该会引导您朝着正确的方向前进。

在运行时之前,m参数的值是未知的,因此,当编译器专门化函数时,它必须为if m==ma和else分支生成代码。 然后它会抱怨,因为它无法理解如果您碰巧调用C::algoa、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> );
}
m参数的值在运行时之前是未知的,因此编译器在专门化函数时必须为if m==ma和else分支生成代码。 然后它会抱怨,因为它无法理解如果您碰巧调用C::algoa、mb或类似的代码,它应该做什么

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


顺便说一句,这是多态性。Polymorfishm有什么不同吗Polly,更多的鱼?嗯?@Als:我没有检查标题:-@LightnessRacesinOrbit哦,天哪,我笑了!现在我的屏幕上有牛奶了。@justik:你确实没有。我注意到你在问题正文中拼写正确,所以我认为这是一个拼写错误,并为你更正了。希望你不介意,即使你介意,我也不会把它改回给罗科的波利提供鱼@Als:他在问题体中正确拼写了一次,错误拼写了一次。顺便说一句,这是多态性。Polymorfishm有什么不同吗Polly,更多的鱼?嗯?@Als:我没有检查标题:-@LightnessRacesinOrbit哦,天哪,我笑了!现在我的屏幕上有牛奶了。@justik:你确实没有。我注意到你在问题正文中拼写正确,所以我认为这是一个拼写错误,并为你更正了。希望你不介意,即使你介意,我也不会把它改回给罗科的波利提供鱼@Als:他在问题正文中正确拼写了一次,错误拼写了一次。@Jon:谢谢,但我需要一个函数,不能两次编写相同的代码算法。@justik,也许你应该简单地将f2重命名为f1。那么algo就是对f1的调用。template static void f1 A&A{}template static void f1 B&B{}@Aaron McDaid:好的,但是情况有点不同。在长算法中,我需要使用f1或f2将结果存储在两个结构a或B中。我只需要一个函数体…@justik:在所有情况下都需要使用两种结构,或者根据参数使用其中一种结构?这些是非常不同的目标。你的问题是不清楚你的要求是什么。对我来说,Aaron在评论中的建议似乎非常有意义。@Jon:谢谢,但我需要一个函数来避免两次编写相同的代码算法。@justik,也许你应该简单地将f2重命名为f1。那么algo就是对f1的调用。template static void f1 A&A{}template static void f1 B&B{}@Aaron McDaid:好的,但是情况有点不同。在长算法中,我需要使用f1或f2将结果存储在两个结构a或B中。我只需要一个函数体…@justik:在所有情况下都需要使用两种结构,或者根据参数使用其中一种结构?这些是非常不同的目标。你的问题是不清楚你的要求是什么。在我看来,Aaron在评论中的建议非常有意义。@jisaak:谢谢,但是算法阻止了这种情况。因此,A、B tzpes的算法几乎相同,2种类型没有两种不同的算法。区分A和B的条件不是“M”吗?@jisaak:我编译了代码,编译器发现了一些错误:不是isClassA,而是某些类型isClassA的专用化?我可以问你一个完整的例子吗,现在我从我的问题中得到了相同的错误代码。。。谢谢。您必须将iClass的第二个签名更改为:template<>struct isClassA>:public true_type{};@谢谢,但是算法阻止了这种情况。因此,A、B tzpes的算法几乎相同,2种类型没有两种不同的算法。区分A和B的条件不是“M”吗?@jisaak:我编译了代码,编译器发现了一些错误:不是isClassA,而是某些类型isClassA的专用化?我可以问你一个完整的例子吗,现在我从我的问题中得到了相同的错误代码。。。谢谢。您必须将iClass的第二个签名更改为:template<>struct isClassA>:public true_type{};@大卫·罗德里格斯。我可以问你一些例子吗:案例B]和案例C]?非常感谢您的帮助。@David Rodriguez:真的很有帮助,再次感谢您。。。我正在研究第二个例子…@David Rodriguez。我可以问你一些例子吗:案例B]和案例C]?非常感谢您的帮助。@David Rodriguez:真的很有帮助,再次感谢您。。。我正在学习第二个例子…@pqnet:谢谢你的帮助。但是我想,指向函数模板的指针是不允许的…@pqnet:谢谢你的帮助。但我认为,指向函数模板的指针是不允许的。。。