Ленивый класс оценки оболочки в C ++? - PullRequest
0 голосов
/ 26 января 2019

Какой хороший способ реализовать класс-оболочку для ленивых вычислений CRTP?

В частности, я рассчитываю работать с отложенной или ленивой оценкой, аналогичной ответам на этот вопрос , и мне интересно обернуть производные классы CRTP в общий класс, чтобы я мог сделать что-то вроде следующего :

Wrapper a = 1;      // Some type like Number<int>
Wrapper b = 2;      // Some type like Number<int>
Wrapper c = a + b;  // An expression like Add<Number<int>, Number<int>>

Я думаю, что умная оболочка указателя имеет смысл, потому что я могу использовать абстрактный базовый класс для базы CRTP и управлять базовыми объектами. До сих пор я реализовал абстрактный класс Base, который имеет enable_shared_from_this, чтобы я мог создать класс shared_ptr и CRTPBase, который обрабатывает шаблонные выражения и типы.

class Base : public std::enable_shared_from_this<Base> {
public:
  virtual ~Base() {}
  // ... other virtual methods.

  // when we don't have the type.
  inline std::shared_ptr<Base> as_ptr() {
    return shared_from_this();
  }
};

template<typename Derived>
class CRTPBase : public Base {
public:
  // ... overridden virtual methods.

  inline Derived &derived() { 
    return static_cast<Derived &>(*this); 
  }

  inline const Derived &derived() const { 
    return static_cast<const Derived &>(*this); 
  }

  // ... other CRTP stuff.
};

Затем производные выражения возвращаются как что-то вроде:

template<typename T1, typename T2>
class Add : public CRTPBase<Add<T1, T2>> {
private:
  const T1 &lhs_;
  const T2 &rhs_;

public:
  Add(const T1 &lhs, const T2 &rhs) : lhs_(lhs), rhs_(rhs) {}
}

template<typename T1, typename T2>
inline const Add<T1, T2> operator+(const CRTPBase<T1> &lhs, const CRTPBase<T2> &rhs) {
  return Add<T1, T2>(lhs.derived(), rhs.derived());
}

Я ищу класс Wrapper, который может принимать что-то вроде Number<T>, Matrix<T> или Add<T1, T2> или что-либо, производное от CRTPBase.

class Wrapper : public CRTPBase<Wrapper> {
private:
  std::shared_ptr<Base> ptr_;

public:
  // rule of zero?

  // example constructor to make a new Number<int>
  explicit inline Wrapper(int value) 
      : ptr_(std::make_shared<Number<int>>(value)) {}

  // what do these look like?
  template<typename T> Wrapper(const CRTPBase<T> &m) { ... }
  template<typename T> Wrapper(CRTPBase<T> &&m) { ... }  
  template<typename T> Wrapper &operator=(const CRTPBase<T> &m) { ... }
  template<typename T> Wrapper &operator=(CRTPBase<T> &&m) { ... }  
};

И поскольку оболочка также является производной от CRTPBase, я могу передать ее в выражения, как и любой другой производный тип. Целью здесь является использование обертки, а не фактических типов.

Как можно обернуть классы CRTP, возвращаемые из выражений, в интеллектуальный указатель? Я не очень уверен в умных указателях и ищу помощь в понимании того, как будет выглядеть их использование в этом классе-обёртке.

В настоящее время у меня есть такие вещи:

template<typename T> Wrapper(const CRTPBase<T> &&m) 
    : ptr_(std::make_shared<T>(std::move(m.derived()))) {}

Что работает (и я не понимаю почему), но, похоже, неправильно.

1 Ответ

0 голосов
/ 02 февраля 2019

Я собираюсь опубликовать то, что сработало для меня, чтобы заставить класс-оболочку работать без использования по умолчанию полиморфного базового класса.

Из ответа на вопрос об обзоре кода SO, который я разместил здесь , в котором предлагалось использовать концепцию полиморфизма времени выполнения Шона Родителя, я смог исключить полиморфный базовый класс CRTP Base.

template<typename Derived>
class CRTPBase {
public:
  inline Derived &derived() { 
    return static_cast<Derived &>(*this); 
  }

  inline const Derived &derived() const { 
    return static_cast<const Derived &>(*this); 
  }

  // ... other CRTP stuff.
};

И я заставил класс Add использовать семантику копирования для безопасности.

template<typename T1, typename T2>
class Add : public CRTPBase<Add<T1, T2>> {
private:
  const T1 lhs_;
  const T2 rhs_;

public:
  Add(const T1 &lhs, const T2 &rhs) 
      : lhs_(lhs), 
        rhs_(rhs) {}

  // ...
}

Затем может быть создан класс-обертка, который имеет полиморфный вложенный класс, скрывающий типы.

class Wrapper : public CRTPBase<Wrapper> {
private:
  struct Concept {
    virtual ~Concept() = default;

    // ... virtual implementation details.
  };

  template<typename T>
  struct Model final : public Concept {
    T data_;

    Model(T data)
        : data_(std::move(data)) {}

    // ... virtual overrides.
  };

  std::shared_ptr<const Concept> ptr_;

public:
  template<typename T>
  inline Wrapper(const CRTPBase<T> &value) 
    : ptr_(std::make_shared<Model<T>>(value.derived())) {}

  // ... functions to interface with ptr_.
};

Это позволяет мне получить нужную семантику, не форсируя абстрактный CRTPбазовый класс, чтобы носить с собой.Это означает, что у меня есть типы Add<Wrapper, Wrapper>, но я могу использовать статический полиморфизм в своем собственном коде, а код времени выполнения использует неинтрузивную форму полиморфизма времени выполнения, как описано Шоном Родителем.

Я до сих пор не знаю 'не знает, как оценивать выражения из оболочек, главным образом потому, что типы, содержащиеся в классе Wrapper, теперь Concept с или Model<T> с, поэтому я все еще сталкиваюсь с проблемами во время фактической оценки, так что если кто-то может прокомментироватьэто, было бы очень цениться:)

...