3

私は C++11 用の JSON クラスを作成しています。 http://github.com/nlohmann/jsonを参照してください。私の中心的なデータ構造は、JSON 値の型 (null、array、object、string、bool、number) を和集合でラップし、Nice C++ インターフェイス経由で提供するクラスです。配列 ( 経由で実装std::vector) とオブジェクト ( ) には独自の反復子が付属しているため、呼び出しをそれぞれのメンバー変数にstd::map委任する「ラッパー」反復子を実装しました。さらに、JSON オブジェクトのキーにアクセスするための 2 つの追加関数と、 へのエイリアスを実装しました。operator++operator->std::string key()reference value()operator*()

これまでのところ、とても良いです (完全なソース コードについては、 https://github.com/nlohmann/json/blob/master/src/json.hppを参照してください)...

reverse_iterator次に、実装したいと思いましたconst_reverse_iterator。そしてここで問題が始まります。

  • using reverse_iterator = std::reverse_iterator<iterator>;と を介してそれらを認識した場合、すべて問題ありませんusing const_reverse_iterator = std::reverse_iterator<const_iterator>;が、関数key()とはまたはオブジェクトでは使用できません。value()reverse_iteratorconst_reverse_iterator
  • reverse_iteratorのような独自のクラスclass reverse_iterator : public std::reverse_iterator<typename basic_json::iterator>を実装すると、クラス全体を再度実装する必要があります。key()との実装を提供するだけでは十分ではありませんが、アダプターを使用して無料で入手したいと思っている と の他のすべてのものvalue()も提供します。operator++()std::reverse_iterator

答えを探すのにかなりの時間を費やしましたが、見つかったすべての参考文献は、不完全なおもちゃの例の表面を引っ掻いたり、反復子は大変な作業であり、Boost に移動する必要があるという結論に達しています...

だからここに私の質問があります:

  1. できるだけ多くの関数を継承するようにreverse_iterator、カスタマイズしたクラスからを作成するにはどうすればよいですか?iterator
  2. 標準のものを超えた動作の継承が自動的に機能しない場合、reverse_iterator完全に繰り返さずに a を作成するにはどうすればよいですか?

どんな助けでも大歓迎です!

4

1 に答える 1

1

残念ながら、私は答えを受け取らなかったので、ここに私がやったことがあります。たぶん、醜い解決策は、誰かがもっと良いものを投稿するように促します:-)

バックグラウンド

したがって、私が最初に学んだことはstd::reverse_iterator、「通常の」イテレータ ( と呼ばれcurrent、 経由でアクセス可能base()) をカプセル化し、逆イテレータが「通常の」イテレータよりも「左に 1 要素多い」という関係を確立することだけです。

イテレータと逆イテレータの関係

( cppreference.comからの画像)

一般的なソリューション

「通常の」反復子が標準インターフェースを持ち、追加の関数を使用しない限り、行は

using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;

独自の反復子クラスを逆反復子に自動的に変換するには十分です。

そしてメンバー関数で

reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }

逆の反復が可能です。たとえば、次のようなコードです

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)
{
   // rit will iterator container c in reverse order
}

動作します。

イテレータに機能を追加する

質問に書いたように、iterator2 つの追加メンバーでクラスを拡張しkey()value(). 前者では、反復中に JSON オブジェクトのキーにすばやくアクセスできます。it.value()後者はではなく書くエイリアスです*it。残念ながら、上記のアプローチはこれらの機能を に継承しませんreverse_iterator

ユーザー定義の逆方向反復子を強化するにはstd::reverse_iterator<iterator>、基本反復子から呼び出しを継承して委任する必要があります。残念ながら、これを手動で行う以外の方法は見つかりませんでした。上記の関数の場合、これは次のようになります。

class reverse_iterator : public std::reverse_iterator<iterator>
{
  ...

  std::string key() const
  {
      auto it = --this->base();
      return it.key();
  }

  reference value() const
  {
      auto it = --this->base();
      return it.operator * ();
  }  
}

最もトリッキーな部分は、"off-by-one" 関係を手動で実装する必要があることです:

  1. 経由でベース イテレータを取得しますbase()
  2. 「右」(実際には左) 要素を指すようにデクリメントします。
  3. 目的の関数を呼び出します。

これで、ほぼ完了です。ほぼ、...上記のコードのためです。operator++あとは、基本クラスなどの関数への他のすべての呼び出しを委譲するだけです。私は、この退屈な代表団に他の誰かを任せる方法を見つけました。

したがって、クラスには次のようなコードが含まれます

using base_iterator = std::reverse_iterator<iterator>;

reverse_iterator operator++(int)
{
    return base_iterator::operator++(1);
}

reverse_iterator& operator++()
{
    base_iterator::operator++();
    return *this;
}

( の定義に注意してくださいbase_iterator。)

それでおしまい。コーディングを可能にするユーザー定義のリバース イテレータができました。

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)
{
   std::cout << rit.key() << '\n';
}

清掃

Github での議論で、gregmarrreverse_iteratorはとconst_reverse_iteratorクラスを次のような単一のテンプレート クラスに結合することを提案しました。

template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>
{
  public:
    /// shortcut to the reverse iterator adaptor
    using base_iterator = std::reverse_iterator<Base>;
    /// the reference type for the pointed-to element
    using reference = typename Base::reference;

    /// create reverse iterator from iterator
    json_reverse_iterator(const typename base_iterator::iterator_type& it)
        : base_iterator(it) {}

    /// create reverse iterator from base class
    json_reverse_iterator(const base_iterator& it) : base_iterator(it) {}

...
}

これにより、書き込みが可能になります

using reverse_iterator = json_reverse_iterator<iterator>;
using const_reverse_iterator = json_reverse_iterator<const_iterator>;

そして幸せになるために。

完全な例

完全なコードについては、こちらを参照してください。

ほとんどの機能を繰り返さないようにする解決策を見たいと思っていますが、これで十分です。そして、しばらくこれ以上のものを見つけられなかったので、共有することにしました。

于 2015-07-21T20:07:23.120 に答える