残念ながら、私は答えを受け取らなかったので、ここに私がやったことがあります。たぶん、醜い解決策は、誰かがもっと良いものを投稿するように促します:-)
バックグラウンド
したがって、私が最初に学んだことは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
}
動作します。
イテレータに機能を追加する
質問に書いたように、iterator
2 つの追加メンバーでクラスを拡張し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" 関係を手動で実装する必要があることです:
- 経由でベース イテレータを取得します
base()
。
- 「右」(実際には左) 要素を指すようにデクリメントします。
- 目的の関数を呼び出します。
これで、ほぼ完了です。ほぼ、...
上記のコードのためです。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>;
そして幸せになるために。
完全な例
完全なコードについては、こちらを参照してください。
ほとんどの機能を繰り返さないようにする解決策を見たいと思っていますが、これで十分です。そして、しばらくこれ以上のものを見つけられなかったので、共有することにしました。