1

バックグラウンド

C++11 では、範囲ベースの for ループは、こちら (リンク) で概説されている 3 種類の「範囲」を処理します。以下、該当部分を引用しました。

構文

for (range_declaration : range_expression) loop_statement

説明

上記の構文は、次のようなコードを生成します ( __range__beginおよび__end説明のみを目的としています)。

{
     auto && __range = range_expression;
     for (auto __begin = begin_expr,
         __end = end_expr;
         __begin != __end; ++__begin) {
         range_declaration = *__begin;
         loop_statement
     }
}

が評価され、range_expression反復されるシーケンスまたは範囲が決定されます。シーケンスの各要素は逆参照され、 で指定された型と名前を使用して変数に割り当てられますrange_declaration

begin_exprとは、次のend_exprいずれかになるように定義されています。

  • (__range)が配列の場合、(__range)および(__range + __bound)、ここで__boundはバインドされた配列です。
  • (__range)がクラスで、begin または end メンバー (または両方) を持つ場合、begin_expris__range.begin()およびend_expris __range.end();
  • それ以外の場合、begin(__range)およびは、関連付けられた名前空間としてend(__range)引数依存のルックアップ ルールに基づいて検出されます。std

質問

引用された箇条書きリストでまったく同じ 3 つのケースを処理する 3 つの特殊化を持つテンプレート関数をどのように記述すればよいですか?

私は次のようなことを考えていますが、これを正しく行う方法がわかりません。

//Handle third bullet - default case
template <typename Range>
void f(Range& range)
{
    for (auto it = begin(range); it != end(range); ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

//Handle first bullet - "range" is an array
template <>
void f<T[]>(T[] range)
{
    auto end = range + sizeof(range)/sizeof(*range);
    for (auto it = range; it != end; ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

//Handle second bullet - "range" with "begin" and/or "end" function
template <>
void f<RangeBE>(RangeBE& range)
{
    //Somehow restrict type RangeBE to classes that have
    //begin or end member functions...
    //Can enable_if be used for this?

    for (auto it = range.begin(); it != range.end(); ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

私はこれに完全に間違ってアプローチしていますか? これは C++11 でも可能ですか (つまり、コンパイラはこの種の特殊化を達成するために何か特別なことをする必要がありますか)? 教えてください。:)

いくつかの説明...

私の質問は、私が書いた関数への入力として(引用された箇条書きリストから)「範囲」の3つの可能なタイプすべてを処理する方法です。(ここで得た知識を使用して、同じ方法で修飾されたクラスを実際に作成します。このクラスには、3 つのタイプの範囲すべてを処理する特殊化があります。)template <typename Range>

私の質問は、「範囲」の 3 つの可能なタイプ (引用された箇条書きリストから) のいずれかを満たすクラスを作成する方法ではありません。それについては複数のSOの質問があります-これはそれらの重複ではありません。

私の質問は、範囲ベースの for ループの使用方法ではありません。

「範囲ベースのforループを使用するだけf」はオプションでも答えでもありません。私が実際にやろうとしているのは、範囲ベースの for ループのセマンティクスを模倣することですが、この質問の範囲外の複雑さのため、サンプル関数内で範囲ベースの for ループを単純に使用することはできませんf「範囲ベースの for ループを使用するだけ」ではない正当な理由があるという事実を受け入れてください。その理由を説明するには複雑すぎるでしょう。

4

1 に答える 1

5

範囲ベースの for ループのセマンティクスを模倣する最も簡単な方法は、範囲ベースの for ループを使用することです。しかし、あなたの明確化によりそれはオプションではなくなり、あなたが何をするつもりなのか疑問に思っているので、別のアプローチがあります:

期待するセマンティクスをすでに示しており、プレーンなC++11std::beginで記述されているandを使用します。範囲ベースの for ループのセマンティクスの 3 番目の箇条書きを模倣するために、ADLを有効にすることを忘れないでください。最終的には次のようになります。std::end

template<typename Range>
void f(Range& range)
{
    using std::begin;
    using std::end;

    for(auto it = begin(range), end_it = end(range); it != end_it; ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

どのようstd::begin/endに機能するのか疑問に思っている場合は、それらの宣言を見てください。

template< class C >
auto begin( C& c ) -> decltype(c.begin());

このオーバーロードは、戻り値の型を推測するための式を含む関数テンプレートです。SFINAEにより、式が実際の型に置き換えられた直後のコンテキストでエラーになるC場合、オーバーロード セットから単純に破棄されます (あたかも存在しないかのように)。

template< class T, size_t N >
T* begin( T (&array)[N] );

このオーバーロードは、配列のケースを処理します。不完全なサイズの配列を扱っているため、アプローチが正しくないことに注意してください。それらのいずれかで要素の数を取得する方法はありません。

于 2013-06-18T04:11:30.703 に答える