【发布时间】:2011-08-28 01:16:23
【问题描述】:
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 的命名空间中有一个“更具体”的 begin/end,它将通过 ADL 选择,否则代码“默认”为 std::begin/end。
但是,ranged-for 有这个特殊例外是有原因的。如果在命名空间中传递一个类型的数组,其带有一个指针的语义不同的 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 开始更好的解决方案?
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-declaration 和 ADL 选择的类型?
【问题讨论】:
-
我花了大约 30 分钟来思考和写下这个问题,但这是我脑海中酝酿了一段时间的事情,我认为只是写它给了我一个解决“ decltype 考虑本地使用声明”,但它并不漂亮。如果今天没有人提及,我会自行回答。
-
我觉得还有一个问题:难道 my::begin 不会被考虑为“return begin(c);”,如果选择的话,是无限递归吗?
-
@Fred:第二个问题很简单,只需要在单独的“detail”命名空间中添加一个额外的函数包装器。
-
返回
typename T::iterator怎么样? -
就我个人而言,我会寻找一个特质——比如
typename range_traits<T>::iterator。这将需要定义range_traits类,但是处理一般情况的range_traits类不会太难编写..(可惜我们在c++0x 中没有概念了)
标签: c++ templates c++11 argument-dependent-lookup