C ++ полиморфизм во время компиляции - PullRequest
1 голос
/ 22 января 2012

Там две несвязанные структуры А и В

template <typename T>
struct A {};

template <typename T>
struct B {};

один тип enum

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

Статический метод algo содержит некоторый довольно сложный алгоритм ... Он изменил некоторые значения и результаты в структуре A или B.

Я бы хотел запустить статический метод algo с объектами 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 ]

3 голосов
/ 22 января 2012

Похоже, что вы используете 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 );
}

Опять же, заставить работать с классом немного сложнее, так как, вероятно, потребуется парапредварительных деклараций, и у меня нет компилятора под рукой, но эскиз должен привести вас в правильном направлении.

2 голосов
/ 22 января 2012

Нормальная перегрузка функции достаточна:

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

Хотя неясно, что вы выиграете от такой договоренности.

0 голосов
/ 22 января 2012

значение параметра m неизвестно до времени выполнения, поэтому компилятор должен генерировать код для ветвей if (m == ma) и else, когда он специализируется на функции. Затем он жалуется, так как не может понять, что он должен делать, если вам случится позвонить C::algo(a,mb) или тому подобное.

Как предположил Джон, перегрузка должна исправить ваш случай, попробуйте использовать этот код:

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> );
}
0 голосов
/ 22 января 2012

Если вам действительно нужно сделать это в одной функции, вы можете использовать типизацию:

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