【问题标题】:Lazy evaluation wrapper class in C++?C ++中的延迟评估包装类?
【发布时间】:2019-06-19 21:21:14
【问题描述】:

为 CRTP 惰性求值实现包装类的好方法是什么?

具体来说,我希望使用类似于this question 的答案的延迟或延迟评估,并且有兴趣将 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 基础使用抽象基类并管理底层对象。 到目前为止,我已经实现了一个具有enable_shared_from_this 的抽象Base 类,这样我就可以创建一个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&lt;T&gt;Matrix&lt;T&gt; Add&lt;T1, T2&gt; 或任何源自的类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()))) {}

哪个有效(我不明白为什么),但似乎是错误的。

【问题讨论】:

  • 要编译std::make_shared&lt;T&gt;(std::move(m))T 需要一个接受CRTPBase&lt;T&gt; 的构造函数,它不会有一个隐式的——你必须明确地添加它。显示 Add 类的样子。
  • 应该起作用的是ptr_(std::make_shared&lt;T&gt;(static_cast&lt;const T&amp;&amp;&gt;(m)))。这将调用T 的移动构造函数,它可以被隐式定义。这依赖于CRTPBase&lt;T&gt; 不会单独出现,而是始终作为T 的基类子对象的假设,因此向下转换是安全的。
  • 添加了更多代码以使实现更加清晰。
  • 嗯,您使用的正是我所想的static_cast——它就在derived() 内部,而不是明确地拼写出来。哪一部分你不明白,哪一部分看起来不对?
  • 这是调用 T 的移动构造函数吗?对我来说很奇怪,而且我的阅读方式略有不同,不会是ptr_(std::make_shared&lt;T&gt;(static_cast&lt;const T&amp;&gt;(m)))吗?

标签: c++ c++11 lazy-evaluation template-meta-programming


【解决方案1】:

我将发布对我有用的东西,以使包装类工作而不默认为多态基类。

从我发布的一个 SO Code Review 问题的答案here,它建议使用 Sean Parent 的运行时多态性概念,我能够消除 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&lt;Wrapper, Wrapper&gt; 类型,但我可以在自己的代码中使用静态多态性,并且运行时代码使用 Sean Parent 所描述的非侵入式运行时多态性。

我仍然不知道如何评估来自包装器的表达式,主要是因为 Wrapper 类中保存的类型现在是 Concepts 或 Model&lt;T&gt;s,所以我在实际评估过程中仍然遇到问题,所以如果有人可以对此发表评论,将不胜感激:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-03
    • 1970-01-01
    • 2021-05-17
    • 2014-12-24
    • 2015-11-12
    • 2016-06-20
    • 1970-01-01
    • 2020-04-19
    相关资源
    最近更新 更多