#c #templates
#c #шаблоны
Вопрос:
предположим, у меня есть объект, подобный этому:
class Spline {
public:
Spline(std::size_t const dim);
// Quite a few functions here. One of those:
vec operator()(double const t) const; // Returns vector of dimension d
}
Теперь, при большинстве применений этого класса, измерение уже будет определено во время компиляции, поэтому было бы хорошей идеей (из соображений производительности) изменить класс следующим образом:
template <std::size_t dim>
class Spline {
public:
Spline();
// Quite a few functions here. One of those:
vec::fixed<dim> operator()(double const t) const; // Returns vector of dimension d
}
(Для тех, кому интересно, vec
и vec::fixed
являются объектами, определенными библиотекой линейной алгебры armadillo). Теперь я хотел бы, чтобы обе версии работали параллельно, что позволило бы выбирать измерение как во время компиляции, так и во время выполнения. Короче говоря, я хотел бы создать эквивалент vec::fixed<dim>
as Spline::fixed<dim>
, но без повторной реализации всех функций. В частности, мне пришлось бы выбирать возвращаемый тип всех этих функций в зависимости от того, присутствует ли аргумент шаблона или нет.
Есть ли у вас какие-либо идеи, как я мог бы этого добиться, особенно с точки зрения понятного и поддерживаемого дизайна? (В надежде, что я ясно выразился, в чем я не совсем уверен.)
Ответ №1:
Используйте простую метаструктуру признаков и специализируйте ее.
template<std::size_t dim>
struct spline_return_traits{
typedef vec::fixed<dim> type;
};
template<>
struct spline_return_traits<0>{ // 0 is a special marker for runtime dimensions
typedef vec type;
};
template<std::size_t dim>
class Spline_impl{
typedef typename spline_return_traits<dim>::type spline_return;
public:
spline_return operator()(double const t) const;
// if <dim> is 0, then the dynamic vec will be chosen as the return type
// all your functions
};
class Spline : public Spline_impl<0>{ // default is dynamic
public:
template<int dim>
struct fixed : public Spline_impl<dim>{
};
};
Теперь вы просто используете это. 🙂 Каждый оператор, конструктор и функция Spline_impl
должны быть доступны в подклассах. Для реализации каждой функции вам необходимо выполнить некоторое ветвление, при котором необходимо выбирать между временем выполнения или фиксированным vec
:
if(dim == 0){
// runtime specific stuff
}else{
// compile-time specific stuff
}
Использовать как:
Spline dynamic_spline;
Spline::fixed<10> fixed_10_spline;
Единственная проблема в том, что Spline
класс будет вдвое больше Spline_impl
… :/ Дай мне подумать, смогу ли я найти решение и для этого.
Редактировать: Если вы не хотите, чтобы размер Spline
был в два раза больше Spline_impl
, одна из возможностей — добавить немного подробностей и typedef:
class Spline : public Spline_impl<0>{ // default is dynamic size
public:
template<std::size_t dim>
struct fixed{
typedef Spline_impl<dim> type;
};
};
И использовать как
Spline dynamic_spline;
typename Spline::fixed<10>::type fixed_10_spline;
Комментарии:
1. Разве это не должно быть
typename Spline::fixed<10>::type fixed_10_spline;
?2. Спасибо. Я каждый день узнаю что-то новое — на этот раз вы сделали это возможным 😉
Ответ №2:
Если я правильно понимаю ваш вопрос, вам также требуется struct
для использования во время компиляции и во время выполнения с предпочтительно одинаковым именем. На мой взгляд, вы можете объявить class
как template
, а затем специализировать один из его экземпляров (скажем, size_t = 0xffffffff), который вы, возможно, не используете. Вы можете объявить все свои определения для использования во время выполнения в этом экземпляре.
Например,
template<std::size_t dim = ~0> // choose default dimension(0xffffffff) not to be used
class Spline {
public:
Spline () {}
vec::fixed<dim> operator () (double const t) const {}
};
template<>
class Spline<~0> { // specialize the default dimension for runtime use
public:
Spline (std::size_t const dim) {}
vec operator () (double const t) const {}
};
Его можно использовать следующим образом:
Spline<5> o5; // compile time dimensions
Spline<> o0(3); // run time dimensions (don't mention anything in template)
Ответ №3:
Вы можете просто перегрузить его. Однако создание неповторяющейся реализации — это проблема, не имеющая универсального решения, если только у вас нет дополнительной магии шаблонов, которая может это сделать.
Комментарии:
1. Возможно ли перегрузить возвращаемый тип?