2013-11-07 3 views
1

Я работаю над кодом и решил добавить поддержку скалярных типов, чтобы иметь возможность переключаться между std::complex<double> и простым double. У меня есть все количество хрустов, написанных, и я использовал его с помощью метода template <typename Scalar>.Предоставить/включить метод для класса в зависимости от типа шаблона

Моя «проблема» заключается в том, что для сложного случая мне нужно будет предоставить некоторые дополнительные методы, которые в реальном случае не имеют смысла и не могут быть закодированы.

Мой предыдущий подход заключался в создании шаблона базового класса, который реализовал все общее. Тогда у меня есть конкретный класс, который вытекает из этого базового класса, с аргументом шаблона Scalarstd::complex<double>. Затем у меня есть тип шаблона прокси/фиктивный класс, который ведет себя как переключатель между версией double и std::complex<double>. Он выглядит более или менее похожим ниже.

//base stuff 
template<typename Scalar> 
class NumberCruncherBase{ 
Scalar stuff1(); 
Scalar stuff2(); 
Scalar stuff3(); 
} 

//inherit base stuff and extend it 
class NumberCruncherComplex : public NumberCruncherBase< std::complex<double> >{ 
std::complex<double> extra_stuff1(); 
} 

//switch proxy 
template<typename Scalar> 
class NumberCruncher : public NumberCruncherBase<Scalar> {} 

//specialization for complex to explicitly derive from the extension 
//in case of complex 
template<> 
class NumberCruncher< std::complex<double> > : public NumberCruncherBase< std::complex<double> > {} 

Удивительно, но или нет этот подход работает kinda good. Вы можете получить от NumberCruncher или непосредственно от конкретного специализированного типа. Также возможно предоставить NumberCRuncherReal для согласования, но это было бы бессмысленно.

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

Я недавно натолкнулся который, кажется, делает то, что мне нужно. Но я не могу заставить его работать. Я устал:

const Matrix op_My(typename enable_if<boost::is_complex<Scalar> >::type* dummy = 0) { return g*H_sum_S[2]; }; 

Это строка в объявлении класса, g++ говорит:

error: expected a constant of type ‘bool’, got ‘boost::is_complex<Scalar>’ 

Мой вопрос, является ли это хороший метод, чтобы добиться того, что я после? и как мне его написать. Я попытался следовать http://www.boost.org/doc/libs/1_54_0/libs/utility/enable_if.html. Я использую gcc 4.8.1.

+0

Если вы не против введения «негативной изменчивости», 'enable_if' /' disable_if' нормально. Что касается ошибки, попробуйте использовать полнофункциональный 'boost :: enable_if'. Обратите внимание, что 'op_My' должен быть шаблоном с аргументом Scalar. –

+1

«Мне нужно предоставить обертку для каждого типа ctor, который у меня есть в базовом классе» [Не в C++ 11 у вас нет] (http://www.stroustrup.com/C++11FAQ.html#inheriting) , –

ответ

2

Конкретная ошибка, которую вы получаете, заключается в том, что enable_if принимает в качестве первого аргумента значение bool, но вы передаете ему тип.

boost::is_complex<Scalar> // <- this is a type 

Чтобы получить булево значение (истина/ложь), вы должны написать:

boost::is_complex<Scalar>::value // <- this is a bool value telling whether Scalar is complex 

is_complex структура наследует от true_type или false_type (в зависимости от типа скаляра), искать их, если вы хотите знать, как это работает более подробно;)

Есть и другие проблемы с вашим кодом. Функция enable_if должна зависеть от параметра шаблона, который сначала известен при вызове функции, а не параметра шаблона класса. Вы могли бы сделать что-то вроде этого:

template<typename Scalar> 
class matrix 
{ 
//... 
    public: 
    // print function that will be called for a matrix of complex numbers 
    template<typename T=Scalar 
      , typename std::enable_if<boost::is_complex<T>::value,int>::type = 0 
      > 
    void print() const; 

    // print function that will be called for a matrix of non-complex numbers 
    template<typename T=Scalar 
      , typename std::enable_if<!boost::is_complex<T>::value,int>::type = 0 
      > 
    void print() const; 
    //... 
}; 

Это произведет «переключатель», который выбирает соответствующий метод класса на основе параметров шаблона класса. Я выбрал по соглашению поместить параметр enable_if в параметры шаблона, а не в подпись функции. Я считаю это более общим решением и более читаемым.

Независимо от того, является ли это «лучшим» способом предоставления этой функции, я не знаю (не могу на моей голове не думать о каких-либо серьезных недостатках), но это будет трюк.Надеюсь, что это помогает :)

Edit 08/11/13:

Я использую специфический вид enable_if структуры, потому что это позволяет мне иметь переключатель enable_if между двумя функциями, которые в противном случае пришлось бы точно такой же подпись. Один из «обычных» способов использования enable_if является использование результата из enable_if в качестве значения по умолчанию для параметра шаблона, например:

template<typename Scalar> 
class matrix 
{ 
//... 
    public: 
    // print function that will be called for a matrix of complex numbers 
    template<typename T=Scalar 
      , class = typename std::enable_if<boost::is_complex<T>::value>::type // enable_if is used to give default type for the class template 
      > 
    void print() const; 

    // print function that will be called for a matrix of non-complex numbers 
    template<typename T=Scalar 
      , class = typename std::enable_if<!boost::is_complex<T>::value>::type // same as above 
      > 
    void print() const; //<- this has same function signature as the above print() 
         // we get a compiler error 
    //... 
}; 

Компилятор не может различать две функции печати друг от друга, так как они оба шаблонных таким же образом, и имеют ту же сигнатуру, так как они оба видели, как

template<typename T, typename U> 
void print() const; 

в моем примере компилятор не знает, как функция шаблонный, как это диктуется результат от enable_if

template<typename T, ?> 
void print() const; 

и, следовательно, можно сначала увидеть подпись функции при вызове функции и получить значение параметра enable_if. Я выбрал Int по соглашению, но вы также можете использовать пустоты *:

typename std::enable_if<!boost::is_complex<T>::value,void*>::type = nullptr 

с пустотой * вместо междунар, но не просто пустота, как мы не можем иметь пустоты параметра шаблона не типа.

Чтобы сделать то, что вы хотите сделать, вам необходимо предоставить не-шаблонную функцию в производном классе для переопределения абстрактной функции в базовом классе. Для этого вы можете использовать косвенность и сделать что-то вроде:

class matrix_base 
{ 
    public: 
     virtual void print() const = 0; 
}; 

template<typename Scalar> 
class matrix: matrix_base 
{ 
    //... 
    private: 
     // print function that will be called for a matrix of complex numbers 
     template<typename T=Scalar 
      , typename std::enable_if<boost::is_complex<T>::value,int>::type = 0 
      > 
     void print_impl() const; 

     // print function that will be called for a matrix of non-complex numbers 
     template<typename T=Scalar 
      , typename std::enable_if<!boost::is_complex<T>::value,int>::type = 0 
      > 
     void print_impl() const; 

    public: 
     // print function that will override abstract print in base class 
     void print() const { print_impl(); } // <- redirect to one of the print_impl() functions 

     //... 
}; 

Недостатком этого подхода является то, что вам нужно, чтобы обеспечить реализацию print_impl() для каждого возможного типа матрицы, то есть вы не можете просто иметь print_impl () для комплексных чисел, вам также необходимо предоставить один для некомплексных чисел.

Надеется, что это сделало некоторые вещи понятнее :)

+0

Не могли бы вы объяснить, почему второй аргумент в 'enable_if' является' int'? Кроме того, мне любопытно, когда этот член получает экземпляр. Я работаю над классом, который происходит из класса «NumberCruncher». Когда 'Scalar' является' std :: complex', я получаю ошибку, что моя 'op_My' aka. 'extra_stuff1' является чистым виртуальным, но если он создается при вызове, а не после экземпляра класса, то он ожидается. Я хотел использовать 'enable_if' для работы с моим предыдущим подходом, но это может быть невозможно. – luk32

Смежные вопросы