Начиная в общем, плюс decltype, учитывая локальное объявление об использовании - PullRequest
11 голосов
/ 18 мая 2011

В циклическом цикле C ++ 0x есть специальное исключение для обработки массивов (FDIS §6.5.4), и есть две функции, std :: begin и end, которые перегружены для обработки массивов или выбора begin / конец методы. Это наводит меня на мысль, что функция, принимающая универсальную последовательность, может быть написана так, чтобы соответствовать поведению цикла для дальней связи:

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

Если в пространстве имен C есть «более конкретное» начало / конец, оно будет выбрано через ADL, в противном случае код «по умолчанию» будет равен std :: 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!
}

Чтобы избежать этого, есть ли лучшее решение, чем написание моих собственных оберток начала / конца (которые используют 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));
}

Однако, как показывает многоточие выше, я даже не знаю, как написать мой :: begin! Как я могу, для этого decltype, выбрать тип, который будет выбран через локальное объявление using и ADL?

Ответы [ 2 ]

3 голосов
/ 18 мая 2011

Я столкнулся с такой же ситуацией при использовании кортежей:

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

, который принимает как std::tuple и boost::tuple, так и принимает как lvalue, так и rvalues, а не template<typename... Types> auto f(std::tuple<Types...>& tuple) -> /* ??? */.

Этот конкретный случай был раскрыт с помощью класса черт, который фактически предусмотрен Стандартом: std::tuple_element.Как обычно с классами признаков, идея состоит в том, что tuple является протоколом, и все, что хочет соответствовать ему, обеспечит специализацию, например, tuple_element.Так что в моем случае решение уже существовало.

В вашем случае, если бы вы писали библиотеку, я бы рекомендовал написать (и задокументировать) такой класс черт.В коде приложения или других ситуациях я не уверен.

1 голос
/ 18 мая 2011

Вы можете создать специальные массивы самостоятельно Тип массива (и должен быть для начала / конца, чтобы работать) ElementType (&)[Size], поэтому, если вы перегружаете функцию как:

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

он должен вести себя как цикл for.

Если заметить, вам не нужны std::begin и std::end, тогда они тривиальны:

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

(может понадобиться приведение; я использовал его только с вещами, которые требовали указателей, а не с итераторами).

С другой стороны, функции begin и end, принимающие указатели, довольно глупы. Если указанный объект является коллекцией, он, вероятно, должен использовать ссылку.

...