基于范围的 for 循环和 ADL
C++0x 标准工作草案 声明(第 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'
所以它似乎正在考虑 仅重载命名空间 std 和其他关联的命名空间,而不是调用站点范围内的重载(上面列表中的第一个要点)。
这是 gcc 错误,还是我误解了标准?
如果是后者,这是否意味着不可能将 std::pair 对象视为基于范围的 for 循环中的范围(不重载 std::begin() 和 std::end() ,如果我不是不允许写错)?
The C++0x standard working draft states (section 6.5.4) the following about the begin() and end() calls that are implicit in a range-based for loop:
'begin' and 'end' are looked up with
argument-dependent lookup (3.4.2). For
the purposes of this name lookup,
namespace std is an associated
namespace.
The way I read this, this means that the overload resolution set for the calls to begin() and end() includes all of the following:
- all overloads of begin() and end() that are in scope at the location where the range-based for loop is used (in particular, all overloads in the global namespace will be in scope)
- all overloads of begin() and end() in namespace std
- all overloads of begin() and end() in other namespaces associated with their arguments
Is that correct?
g++ 4.6's behaviour does not seem to be consistent with this interpretation. For this code:
#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)
;
}
It gives the following errors:
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>)
This suggests that it is considering only the overloads in namespace std, and not the overloads in the global namespace.
If, however, I use my own pair class declared in the global namespace, it compiles fine:
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)
;
}
As a final test, I tried putting my_pair in a separate namespace:
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)
;
}
And again I get the errors:
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'
So it seems it is considering only overloads in namespace std and other associated namespaces, and not overloads that are in scope at the call site (the first bullet point in my list above).
Is this a gcc bug, or am I misinterpreting the standard?
If the latter, does that mean it's impossible to treat an std::pair object as a range in a range-based for loop (without overloading std::begin() and std::end(), which if I'm not mistaken is not allowed)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我首先报告说这对我来说看起来像是一个 gcc bug。现在看来,连 for 循环规范的这一部分也不清楚,并且已经向委员会提出了质询。
看起来基于范围的 for 循环规则很快就会改变:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf
我不确定 N3257 中列出了哪个选项将被选择。
I first reported that this looked like a gcc bug to me. It now appears that even this part of the for-loop specification is unclear, and an inquiry has been opened on the committee.
It still looks like the range-based for-loop rules are going to change very shortly:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf
And I'm not sure which option listed in N3257 will be chosen.