Динамический выбор типа данных на основе шаблона

#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. Возможно ли перегрузить возвращаемый тип?