【发布时间】:2011-03-04 22:43:39
【问题描述】:
C++0x standard working draft 声明(第 6.5.4 节)以下关于在基于范围的 for 循环中隐含的 begin() 和 end() 调用:
'begin' 和 'end' 被查找 依赖于参数的查找 (3.4.2)。为了 此名称查找的目的, 命名空间 std 是关联的 命名空间。
按照我的阅读方式,这意味着为调用 begin() 和 end() 设置的重载解决方案包括以下所有内容:
- begin() 和 end() 的所有重载都在使用基于范围的 for 循环的位置范围内(特别是,全局命名空间中的所有重载都将在范围内)
- 命名空间 std 中 begin() 和 end() 的所有重载
- begin() 和 end() 在与其参数关联的其他命名空间中的所有重载
对吗?
g++ 4.6 的行为似乎与这种解释不一致。对于此代码:
#include <utility>
template <typename T, typename U>
T begin(const std::pair<T, U>& p);
template <typename T, typename U>
U end(const std::pair<T, U>& p);
int main()
{
std::pair<int*, int*> p;
for (int x : p)
;
}
它给出了以下错误:
adl1.cpp: In function 'int main()':
adl1.cpp:12:18: error: No match for 'begin(pair<int *, int *> &)'
adl1.cpp:12:18: candidate is:
/usr/local/lib/gcc/i686-pc-linux-
gnu/4.6.0/../../../../include/c++/4.6.0/initializer_list:86:38: template<
class _Tp> constexpr const _Tp * begin(initializer_list<_Tp>)
adl1.cpp:12:18: error: No match for 'end(pair<int *, int *> &)'
adl1.cpp:12:18: candidate is:
/usr/local/lib/gcc/i686-pc-linux-
gnu/4.6.0/../../../../include/c++/4.6.0/initializer_list:96:36: template<
class _Tp> constexpr const _Tp * end(initializer_list<_Tp>)
这表明它仅考虑命名空间 std 中的重载,而不考虑全局命名空间中的重载。
但是,如果我使用自己在全局命名空间中声明的 pair 类,它编译得很好:
template <typename T, typename U>
struct my_pair
{
T first;
U second;
};
template <typename T, typename U>
T begin(const my_pair<T, U>& p);
template <typename T, typename U>
U end(const my_pair<T, U>& p);
int main()
{
my_pair<int*, int*> p;
for (int x : p)
;
}
作为最终测试,我尝试将 my_pair 放在单独的命名空间中:
namespace my
{
template <typename T, typename U>
struct my_pair
{
T first;
U second;
};
}
template <typename T, typename U>
T begin(const my::my_pair<T, U>& p);
template <typename T, typename U>
U end(const my::my_pair<T, U>& p);
int main()
{
my::my_pair<int*, int*> p;
for (int x : p)
;
}
我又得到了错误:
adl3.cpp: In function 'int main()':
adl3.cpp:22:18: error: 'begin' was not declared in this scope
adl3.cpp:22:18: suggested alternative:
adl3.cpp:14:35: 'begin'
adl3.cpp:22:18: error: 'end' was not declared in this scope
adl3.cpp:22:18: suggested alternative:
adl3.cpp:17:33: 'end'
所以它似乎在考虑 only 命名空间 std 和其他相关命名空间中的重载,而不是在调用站点范围内的重载(我上面列表中的第一个要点)。
这是一个 gcc 错误,还是我误解了标准?
如果是后者,这是否意味着不可能将 std::pair 对象视为基于范围的 for 循环中的范围(不重载 std::begin() 和 std::end(),如果我'm not wrong is not allowed)?
【问题讨论】:
-
我建议用
foreach重新标记它,因为基于范围的for-loop 只是一个更冗长的术语。 -
“这是否意味着不可能将 std::pair 对象视为基于范围的 for 循环中的范围” - 我怀疑不是,因为即使你 有误解了标准,C++0x 现在对函数模板进行了部分专门化。你不能重载
std::begin,但你可以专门化它。 -
@Steve:你确定 C++0x 对函数模板有部分特化吗?我在草稿中找不到任何提及。
-
@HighCommander4:定义“确定”。但不,我不是,我是在模糊的记忆中工作。由于“类模板部分专业化”对 n3225 中的标题进行评分,并且没有这样的标题“功能模板部分专业化”我猜他们没有做到,所以如果 GCC 的实现是正确的,那么你会被卡住。
-
然而,很少有人真正写
using std::sort; sort(foo.begin(), foo.end());,以便获得您的算法的ADL重载版本。因此,通常客户只有在知道它的情况下才会得到它。你是对的,因此在实践中存在不一致,因为如果Foo只是一个非模板类,那么我们可以潜入std::sort(Foo::iterator, Foo::iterator)的完全专业化,即使他们完全调用所有用户也会得到它 -合格的std::sort(foo.begin(), foo.end())。
标签: c++ foreach c++11 argument-dependent-lookup