Можно ли использовать класс шаблона без типа с CRTP

#c #templates #crtp

Вопрос:

У меня есть следующий базовый класс CRTP:

 template <typename T, template <typename> typename CRTPType>
  struct enable_crtp {
    auto underlying() -> Tamp; { return static_cast<Tamp;>(*this); }
    auto underlying() const -> const Tamp; {
      return static_cast<const Tamp;>(*this);
    }
  };
 

Он безупречно работает с классами такого типа:

 template<typename Derived>
class BaseA : public enable_crtp<Derived, BaseA> {
public:
  void DoA() {
    this->underlying().DoAImpl();
  }
private:
  friend Derived;
};

class ImplA : public BaseA<ImplA> {
  void DoAImpl() {
    // do something 
  }

  friend BaseA<ImplA>;
};
 

Однако есть ли в любом случае способ заставить его работать с классом шаблона, не относящимся к типу? Что-то вроде этого:

 template<template<size_t> typename Derived>
class BaseB : public enable_crtp<Derived<size_t>, BaseB> {
                                         ^ compile error, template argument for non-type 
                                           template parameter must be an expression
  void DoB() {
    this->underlying().DoBImpl();
  }
}
 

Для реальной проблемы у меня есть a IntegralImageCalculator , который будет перебирать пиксели изображения, чтобы накопить сумму, основанную на его i-th order :

   template <size_t Order>
  class IntegralImageCalculator;

  template <>
  class IntegralImageCalculator<1> {
    void Iterate(cv::Matamp; input) {
      // Duplicate code
      // ...

      cv::Mat integral_1st_order;
      cv::integral(input, integral_1st_order);

      input.forEach<double>(
        [amp;integral_1st_order, this](doubleamp; pixel, const int* position) {
          Process(integral_1st_order, pixel, position);
        });

      // Duplicate code
      // ...
    }

    void Process(const cv::Matamp; integral_1st_order,
                 doubleamp; pixel,
                 const int* position) {
      // derived class must implement this
    }
  };

  template <>
  class IntegralImageCalculator<2> {
    void Iterate(cv::Matamp; input) {
      // Duplicate code
      // ...

      cv::Mat integral_1st_order;
      cv::Mat integral_2nd_order;
      cv::integral(input, integral_1st_order, integral_2nd_order);

      input.forEach<double>(
        [amp;integral_1st_order, this](doubleamp; pixel, const int* position) {
          Process(integral_1st_order, integral_2nd_order, pixel, position);
        });

      // Duplicate code
      // ...
    }

    void Process(const cv::Matamp; integral_1st_order,
                 const cv::Matamp; integral_2nd_order,
                 doubleamp; pixel,
                 const int* position) {
      // derived class must implement this
    }
  };
 

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

Комментарии:

1. Зачем вам нужен CRTPType параметр шаблона? В вашем примере он не используется. Если вам это действительно нужно, почему бы не пройти BaseA<Derived> вместо BaseA этого ?

2. С одной стороны, ваш компилятор прав; Derived<size_t> это недопустимый экземпляр вашего шаблона. С другой стороны, задумывались ли вы о том, как бы вы определили IntegralImageCalculator<1> с помощью экземпляра BaseB как базовый класс? Как об этом 1 сообщается enable_crtp ? (Это один из тех случаев, когда пропуск вперед может оказаться полезным трюком-просто не забудьте оглянуться назад, на то, что вы пропустили в какой-то момент.)

3. @Evg Я обновил код, хитрость в том, чтобы избавиться от алмазного наследования, вы можете увидеть больше здесь fluentcpp.com/2017/05/19/crtp-helper

4. Я предлагаю добавить алмазное наследование к вашему вопросу.

5. Ссылка на fluentcpp не имеет никакого смысла. Вводный пример нарушен . CRTP вообще так не работает. template <typename T> struct NumericalFunctions : crtp<T> не имеет ничего общего с CRTP, как мы знаем и любим. Я предлагаю не обращать внимания на эту страницу.

Ответ №1:

В вопросе отсутствуют некоторые детали, но позвольте мне попытаться дать вам одно возможное решение. Даже если это не совсем то, что вам нужно, я надеюсь, что это может вам помочь.

 template<class T>
class Base {
public:
    void Iterate(cv::Matamp; input) {
        std::array<cv::Mat, T::Order> integrals;

        std::apply([amp;input](autoamp;... integrals) { 
            cv::integral(input, integrals...); }, integrals);

        input.forEach<double>([amp;integrals, this](doubleamp; pixel, const int* position) {
            auto Process = [amp;](autoamp;... integrals) {
                static_cast<T*>(this)->Process(integrals..., pixel, position);
            };
            std::apply(Process, integrals);
        });
    }
};

template<std::size_t Order_, class Derived>
class IntegralImageCalculator : public Base<Derived> {
public:
    static constexpr std::size_t Order = Order_;
};

class A : public IntegralImageCalculator<1, A> {
public:
    void Process(const cv::Matamp;, doubleamp; pixel, const int* position) {
        // implementation
    }
};

class B : public IntegralImageCalculator<2, B> {
public:
    void Process(const cv::Matamp;, const cv::Matamp;, doubleamp; pixel, const int* position) {
        // implementation
    }
};
 

Компилируемый пример с тривиальными заглушками для резюме.