5

式テンプレートで使用できる配列ベースのコードがあるとします。たとえば、これらの配列をオーバーロードし、算術演算子などoperator[]もオーバーロードしました。+

any_ofここで、そのような配列でSTL アルゴリズムを実行したいと思います。簡単な方法は、

ExprArray<double, N> b, c; // init etc. 
auto a = b + c;            // for (auto i = 0; i < N; ++i) { a[i] = b[i] + c[i]; }
auto res = std::any_of(begin(a), end(a), SomePred{});

もちろん、私は計算を短絡して、変更された(範囲ベースの)ものを持っていることを望みlib::any_ofます

// only compute b[i] + c[i] until SomePred is satisified
auto res = lib::any_of(b + c, SomePred{}); // write as explicit loop over b[i] + c[i]

オーバーlib::any_ofロードoperator[]された に対して行われたのと同じように、その入力に対して を記述すると、その仕事が行われoperator+ます。ただし、これには、そのような配列で実行できる可能性のあるすべての STL アルゴリズムの同様の再実装が必要になります。

質問: それで、既存の範囲ベースのアルゴリズム (Boost.Range、range-v3)を、ExprArray iterators . ExprArrayイテレータoperator*を変更してoperator++、これが範囲ベースのアルゴリズムに対して透過的になるようにすることは可能ですか?

// only compute b[i] + c[i] until SomePred is satisified
// needs to eventually dispatch to 
// for (auto i = 0; i < N; ++i)
//     if (SomePred(b[i] + c[i])) return true;
// return false;
auto res = ranges::any_of(b + c, SomePred{});

したがって、アルゴリズム バージョンが実際にイテレータに関して実装されている場合、ループは を計算するfor (auto it = first; it != last; ++it)必要*itがあるという事実を認識し、 をb[i] + c[i]実行する必要があることを認識++itしている必要があります++i

4

2 に答える 2

4

この質問は、「式テンプレートにイテレータを実装できますか?」に帰着するようです。これはかなり簡単だと思います。「式テンプレート」がそれらsizeを知っていて、反復子をオーバーロードしていると仮定するoperator[]と、式オブジェクトへの参照とそれが表す範囲へのオフセットを保持する必要があるだけです。

template <class Expr>
class iterator {
public:
  using iterator_category = ranges::random_access_iterator_tag;
  using difference_type = std::ptrdiff_t;
  using value_type = typename Expr::value_type;

  iterator() = default;
  constexpr iterator(Expr& e, difference_type i) :
    expr_{&e}, i_{i} {}

  constexpr bool operator==(const iterator& that) const {
    return assert(expr_ == that.expr_), i_ == that.i_;
  }
  constexpr bool operator!=(const iterator& that) const {
    return !(*this == that);
  }
  // Similarly for operators <, >, <=, >=

  value_type operator*() const {
    return (*expr_)[i_];
  }
  value_type operator[](difference_type n) const {
    return (*expr_)[i_ + n];

  iterator& operator++() & { ++i_; }
  iterator operator++(int) & { auto tmp = *this; ++*this; return tmp; }
  // Similarly for operator--

  iterator operator+(difference_type n) const {
    return iterator{expr_, i_ + n};
  }
  // Similarly for operators -, +=, and -=

  friend iterator operator+(difference_type n, const iterator& i) {
    return i + n;
  }

private:
    Expr* expr_;
    difference_type i_;
};

これで、「式テンプレート」がbeginandendを返すメンバーとiterator{*this, 0}を持つように調整するだけで済みますiterator{*this, size()}

于 2015-08-31T17:42:05.867 に答える
2

ここでの問題は、何b+cが返されるかです。実数を返す場合、ExprArray遅延評価は実際にはできません。配列を埋める必要があります。bandへの参照を保存することはできませんc

ただし、LazyAdditionその変換が加算をExprArray実行するを返す場合は、LazyAddition::iterator遅延加算も実装できることを確認するのは簡単です。ここでのリスクは、オブジェクトではなく、保留中の参照をauto a = b+c持つオブジェクトが作成されることです。LazyAdditionExprArray

ExprArray舞台裏で をスマート ポインターとして実装しようとすると、事態は非常に厄介になります。確かに、Copy-On-Write を実装b+cして、元の両方の配列へのポインターを保持する ExprArray にすることができます。しかしT& ExprArray<T>::operator[]、COW を呼び出すとすぐに起動し、配列全体を 1 つの要素readにコピーします。(C++ 演算子のオーバーロード ルールconstは ではうまく機能しませんoperator[]。読み取りアクセスに使用される場合ではなく、引数自体が const の場合に const バージョンが選択されます)

于 2015-08-31T11:52:13.950 に答える