一般来说,加上考虑本地使用声明的decltype

时间:2011-05-18 12:58:41

标签: c++ templates c++11 argument-dependent-lookup

C ++ 0x的ranged-for循环有一个处理数组的特殊异常(FDIS§6.5.4),并且有两个函数,std :: begin和end,它们被重载以处理数组或选择begin /结束方法。这让我相信可以编写一个接受泛型序列的函数来匹配一个ranged-for循环的行为:

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。

但是,有一个原因有一个特殊例外。如果在命名空间中传递一个类型的数组,并且语义不同的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-declaration和ADL选择的类型?

2 个答案:

答案 0 :(得分: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) -> /* ??? */

这个特殊情况是用特征类解决的,实际上由标准提供:std::tuple_element。与traits类一样,我们的想法是tuple是一个协议,任何想要符合它的东西都会提供一个特殊的例子。 tuple_element。所以在我的情况下,解决方案已经存在。

在您的情况下,如果您正在编写图书馆,我建议您编写(并记录)此类特征类。在应用程序代码或其他情况下,我不太确定。

答案 1 :(得分:1)

您可以自己对阵列进行特殊处理。数组的类型是(并且必须是开始/结束才能工作)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);
}

(可能需要一个演员;我实际上只用于需要指针的东西,而不是任何迭代器)。

另一方面,beginend函数指针是相当愚蠢的事情。如果指向的对象是一个集合,那么它们可能应该引用它。