2012-03-24 11 views
3

は、私がsを使用する必要が言う:std :: find()はCスタイルの配列で後方に移動しますか?

typedef struct tagSOMESTRUCT // Defined by someone else; C-compatible 
{ 
    int count; 
    int elements[256]; 
} SOMESTRUCT; 

SOMESTRUCT s; 

と私はのような機能を持っていると言う:この機能を使用するためには

template<typename RevFwdIt> 
std::pair<RevFwdIt, RevFwdIt> some_slice_rev(RevFwdIt rbegin, RevFwdIt rend) 
{ 
    RevFwdIt it = basename_rev(rbegin, rend); 
    RevFwdIt e = std::find(rbegin, it, 5); 
    return std::make_pair(e == it ? rbegin : e, it); 
} 

、私は

some_slice_rev(&s.elements[s.count - 1], &s.elements[-1]); 

どの言う必要があります(IMHO)は、off-by-oneエラーのために、醜く、エラーを起こしやすい。

一方で、私は単にそれからstd::findは終わりの代わりに最初から検索してしまうため(より良い)

some_slice(&s.elements[0], &s.elements[s.count]); 

を使用するsome_slicesome_slice_revを変更することはできません。

一方、std::findが未処理のポインタである「逆イテレータ」をどのように処理するのかわからないので、コード自体はすでに壊れています。

このような状況でコードを修正する最もよい方法は何ですか? rawポインタである逆イテレータを扱う方法はありますか?または、これを修正するための標準的なリファクタリングメカニズムがありますか?以外はSOMESTRUCTを変更していますか?

答えて

6

は(それはあなたが避けるようにしようとしているように見えるイテレータ方向の厄介な混合からのものであってもよい)が、私はちょうどstd::reverse_iteratorにあなたの注意を向けましょう:

#include <iostream> 
#include <iterator> 

// for example 
template <typename Iter> 
void print_it(Iter first, Iter last) 
{ 
    std::cout << '|'; 

    for (; first != last; ++first) 
     std::cout << ' ' << *first << " |"; 

    std::cout << std::endl; 
} 

int main() 
{ 
    int arr[10] = {1, 2, 3, 4}; 

    int *begin = arr, *end = arr + 4; 

    print_it(begin, end); 
    print_it(std::reverse_iterator<int*>(end), 
       std::reverse_iterator<int*>(begin)); 
} 

++が内部では--であることを除いて、双方向イテレータのように機能し、その逆もあります。

これはちょっと醜いことに注意してください。あなたは、いくつかのユーティリティ機能をお勧めします:

#include <iostream> 
#include <iterator> 

// for example 
template <typename Iter> 
void print_it(Iter first, Iter last) 
{ 
    std::cout << '|'; 

    for (; first != last; ++first) 
     std::cout << ' ' << *first << " |"; 

    std::cout << std::endl; 
} 

template <typename Iter> 
std::reverse_iterator<Iter> make_reverse_iterator(Iter iter) 
{ 
    return std::reverse_iterator<Iter>(iter); 
} 

int main() 
{ 
    int arr[10] = {1, 2, 3, 4}; 

    int *begin = arr, *end = arr + 4; 

    print_it(begin, end); 
    print_it(make_reverse_iterator(end), 
       make_reverse_iterator(begin)); 
} 

だから私はあなたがこのほしいと思う:今比較的オフトピック

template<typename ForwardIterator > 
std::pair<ForwardIterator, ForwardIterator> 
    some_slice(ForwardIterator begin, ForwardIterator end) 
{ 
    typedef std::reverse_iterator<ForwardIterator> rev_iter; 

    rev_iter it = basename(rev_iter(end), rev_iter(begin)); 
    rev_iter e = std::find(rev_iter(end), it, 5); 

    return std::make_pair(it.base(), e.base()); 
} 

を、しかしs.countがある場合s.elements[s.count]は未定義の動作であることに注意してください256s.elements[s.count]*(s.elements + s.count)であるため、逆参照する有効な配列要素ではありません。実際に

&*xxに相殺するので、完全な表現は結構ですが、あなたはおそらくまだそれを回避したい:私は厳密にそれはあるかもしれない話すと思うけれども

some_slice(s.elements, s.elements + s.count); 

s.elements[-1]も、未定義の動作かもしれアレイの前にintのメンバーがあるため、偶発的に合法的になりました。

+0

ああ...うわー。私はそれが内部的にインスタンス化されなければならないと思ったように 'reverse_iterator'をインスタンス化することが合法だとは知らなかった。 +1すると便利です。しかし、フォローアップ: 'some_slice_rev'の周りに' some_slice'ラッパーを作成する一般的な方法を知っていますか? (私は単純に 'some_slice(make_reversed(end-1)、make_reversed(begin-1))'と呼ぶことはできません)。 – Mehrdad

+0

@Mehrdad:私は戻ってくるのでしょうか?それを理解していない。 :)おそらく 'std :: reverse_iterator'が' base() '関数を持っていることを知っていると、根底にあるイテレータを得るために、あなたの質問に答えるのに役立ちます。 – GManNickG

+0

申し訳ありませんが、私はそれをもっと明確にしようとします:問題は、Cスタイルの配列で 'find'を使用できるようにすることですが、配列の最後から始めることです。もちろん、ポインタを使用するCスタイルの配列では機能しない点を除いて、逆のイテレータ( 'rbegin()'と 'rend()')を持つコンテナでも動作します。 ** XYZ_rev'関数を作るのではなく、フォワードイテレータを使ってこれを行う関数 'XYZ'を作成するのが好きですが、良い方法はわかりません。助言がありますか? – Mehrdad

1

リバースイテレータをシミュレートするためにラッパーイテレータクラスを記述してから、生の配列イテレータの代わりにstd::findを使用するのが簡単な解決策です。したがって、std::find++itを呼び出すと、operator++が呼び出され、実際には実際のイテレータである--rawItがデクリメントされます。それが他の標準的なリバース・イテレータの機能です。私は質問を理解し、非常にわからないんだけど

+0

私はそれについて考えていました...それはいわば「標準的な」解決策ですか?それともよく知られている解決策はありませんか? – Mehrdad

+0

@Mehrdad:それは、他の標準的なリバースイテレータがやっていることだと思います。 – Nawaz

+0

ええ、ありがとう、ありがとう。 +1 – Mehrdad

関連する問題