C++0x标准工作草案(第6.5.4节)对于范围for循环中隐式调用begin()和end()的说明如下:
“begin”和“end”使用参数相关查找(3.4.2)进行查找。对于此名称查找,命名空间std是一个关联命名空间。
我理解的意思是,调用begin()和end()的重载分辨集包括以下所有内容:
- 在范围for循环使用的位置作用域内的所有begin()和end()的重载(特别是全局命名空间中的所有重载将在作用域内)
- 命名空间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'
所以,它似乎只考虑了命名空间std和其他相关命名空间中的重载,而不考虑在调用点处范围内的重载(上面列表中的第一个要点)。
这是gcc的错误吗,还是我对标准的解释有误?
如果是后者,这是否意味着无法将std::pair对象视为range在range-based for循环中处理(没有重载std::begin()和std::end(),如果我没记错的话是不允许的)?
std :: begin
,但允许对其进行专门化。 - Steve Jessopstd
之外。但这没关系,因为问题并不是缺乏功能,而是标准(可能是正确的)认为您不应该允许使用标准类更改标准模板函数的行为。由于gcc无法在全局命名空间中找到重载,这是我们最后的和平希望,但只是因为编译器存在缺陷,所以不是标准的错。 - Steve Jessopusing std::sort; sort(foo.begin(), foo.end());
以获得算法的 ADL 过载版本。因此,通常只有当客户知道此功能时才会使用它。您说得对,在实践中存在不一致性,因为如果Foo
只是一个非模板类,那么我们可以偷偷地添加std::sort(Foo::iterator, Foo::iterator)
的完整特化,即使用户调用了完全限定的std::sort(foo.begin(), foo.end())
,所有用户也将获得该特化版本。 - Steve Jessop