11

C++0x の ranged-for ループには、配列を処理するための特別な例外があり (FDIS §6.5.4)、std::begin と end の 2 つの関数があり、配列を処理するため、または begin/end メソッドを選択するためにオーバーロードされます。これにより、一般的なシーケンスを受け入れる関数は、範囲指定された for ループの動作に一致するように記述できると思います。

template<class C>
void f(C &c) {
  using std::begin;
  using std::end;
  do_something_with(begin(c), end(c));
}

C の名前空間に「より具体的な」begin/end がある場合は、ADL を介して選択されます。それ以外の場合、コードは std::begin/end に「デフォルト」設定されます。

ただし、範囲指定には特別な例外がある理由があります。ポインターを受け取る意味的に異なる begin/end を持つ名前空間で型の配列を渡す場合、std::begin/end の配列形式は選択されません。

namespace ns {
  struct A {};
  void begin(A*);  // Does something completely different from std::begin.
}

void f_A() {  // Imagine above f() called with an array of ns::A objects.
  ns::A c[42];
  using std::begin;
  begin(c);  // Selects ns::begin, not array form of std::begin!
}

これを回避するには、独自の begin/end ラッパー (内部で ADL を使用する) を作成し、std::begin または ADLized begin の代わりに明示的に呼び出すよりも良い解決策はありますか?

namespace my {
  template<class T>
  auto begin(T &c)  // Also overload on T const &c, as std::begin does.
  -> decltype(...)  // See below.
  {
    using std::begin;
    return begin(c);
  }

  template<class T, int N>
  T* begin(T (&c)[N]) {
    return c;
  }
}
// my::end omitted, but it is analogous to my::begin.

template<class C>
void f(C &c) {
  do_something_with(my::begin(c), my::end(c));
}

しかし、上記の省略記号で示されているように、my::begin! の書き方さえわかりません。その decltype に対して、ローカルの using 宣言と ADL によって選択される型を選択するにはどうすればよいですか?

4

2 に答える 2

3

タプルの使用中に同じ状況に遭遇しました:

template<typename Tuple>
auto f(Tuple&& tuple)
-> /* ??? */
{
    using std::get;
    return get<Idx>(tuple);
}

と の両方を受け入れstd::tupleboost::tupleとは対照的に、左辺値と右辺値の両方を受け入れますtemplate<typename... Types> auto f(std::tuple<Types...>& tuple) -> /* ??? */

この特定のケースは、実際には Standard: によって提供される traits クラスで解決されましたstd::tuple_element。トレイト クラスでいつものように、これtupleはプロトコルであり、それに準拠したいものはすべて、たとえば の特殊化を提供しtuple_elementます。したがって、私の場合、解決策はすでに存在していました。

あなたの場合、ライブラリを作成している場合は、そのような特性クラスを作成 (およびドキュメント化) することをお勧めします。アプリケーション コードやその他の状況では、よくわかりません。

于 2011-05-18T13:24:16.673 に答える
1

配列を自分で特殊ケース化できます。配列の型は (begin/end が機能するために必要)ElementType (&)[Size]であるため、次のように関数をオーバーロードすると:

template<class C, size_t S>
void f(C (&c)[S]) {
  do_something_with(std::begin(c), std::end(c));
}

for ループのように特別に動作する必要があります。

補足として、必要はありませんstd::beginstd::endそれらは簡単です。

template<class C, size_t S>
void f(C (&c)[S]) {
  do_something_with(c, c + S);
}

(キャストが必要な場合があります。実際には、イテレーターではなく、ポインターを要求するものでのみ使用しました)。

別の補足として、beginポインタendを取る関数はかなりばかげたことです。指摘されたオブジェクトがコレクションである場合、おそらく代わりに参照を取得する必要があります。

于 2011-05-18T13:35:53.297 に答える