15

範囲ベースの for ループは C スタイルの文字列をサポートすると想定していました

void print_C_str(const char* str)
{
    for(char c : str)
    {
        cout << c;
    }
}

ただし、これは当てはまりません。標準で[stmt.ranged] (6.5.4)は、範囲ベースの for は次の 3 つの可能性のいずれかで機能すると述べています。

  1. 範囲は配列です
  2. begin範囲は呼び出し可能なendメソッドを持つクラスです
  3. 関連付けられた名前空間 (およびstd名前空間)に到達可能な ADL があります。

グローバル名前空間に関数を追加するbeginと、まだエラーが発生します(VS12とGCC 4.7の両方から)。endconst char*

範囲ベースの for ループを C スタイルの文字列で動作させる方法はありますか?

オーバーロードを追加しようとしnamespace stdましたが、これは機能しましたが、私の理解では、オーバーロードを追加することは違法namespace stdです (これは正しいですか?)

4

3 に答える 3

21

null で終わる文字列に対して簡単な反復子を作成する場合は、ポインター自体を範囲として扱うのではなく、特別な範囲を返すポインターで関数を呼び出すことによってこれを行うことができます。

template <typename Char>
struct null_terminated_range_iterator {
public:
    // make an end iterator
    null_terminated_range_iterator() : ptr(nullptr) {}
    // make a non-end iterator (well, unless you pass nullptr ;)
    null_terminated_range_iterator(Char* ptr) : ptr(ptr) {}

    // blah blah trivial iterator stuff that delegates to the ptr

    bool operator==(null_terminated_range_iterator const& that) const {
        // iterators are equal if they point to the same location
        return ptr == that.ptr
            // or if they are both end iterators
            || is_end() && that.is_end();
    }

private:
    bool is_end() {
        // end iterators can be created by the default ctor
        return !ptr
            // or by advancing until a null character
            || !*ptr;
    }

    Char* ptr;
}

template <typename Char>
using null_terminated_range = boost::iterator_range<null_terminated_range_iterator<Char>>;
// ... or any other class that aggregates two iterators
// to provide them as begin() and end()

// turn a pointer into a null-terminated range
template <typename Char>
null_terminated_range<Char> null_terminated_string(Char* str) {
    return null_terminated_range<Char>(str, {});
}

使用法は次のようになります。

for(char c : null_terminated_string(str))
{
    cout << c;
}

これで表現力が失われることはないと思います。実はこっちの方がわかりやすいと思います。

于 2013-01-23T10:47:22.320 に答える
3

考えられる回避策は、null で終了する文字列を別の型でラップすることです。最も単純な実装は次のとおりです(呼び出しているため、 R. Martinho Fernandesの提案よりもパフォーマンスが低くなりますstrlenが、コードも大幅に少なくなります)。

class null_terminated_range {
    const char* p:
public:
    null_terminated_range(const char* p) : p(p) {}
    const char * begin() const { return p; }
    const char * end() const { return p + strlen(p); }
};

使用法:

for(char c : null_terminated_range(str) ) 
于 2013-01-23T15:10:54.207 に答える
2

C 文字列は配列ではなく、begin/endメンバーを持つクラスでもありません。また、引数がプリミティブであるため、ADL では何も見つかりません。おそらく、これは、グローバル名前空間で関数を見つける、ADLを使用した単純な非修飾ルックアップである必要があります。でも、言葉遣いからして、ありえないと思います。

于 2013-01-23T10:47:21.637 に答える