基于范围的循环和ADL

时间:2011-03-04 22:43:40

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

C++0x standard working draft状态(第6.5.4节)以下关于在基于范围的for循环中隐含的begin()和end()调用:

  查找“开始”和“结束”   参数依赖查找(3.4.2)。对于   这个名称查找的目的,   namespace 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中的重载,而不是全局命名空间中的重载。

但是,如果我使用自己在全局命名空间中声明的对类,则编译很好:

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和其他相关命名空间中的 only 重载,而不是调用站点范围内的重载(上面列表中的第一个项目符号点)。

这是一个gcc错误,还是我误解了标准?

如果是后者,这是否意味着将std :: pair对象视为基于范围的for循环中的范围是不可能的(不重载std :: begin()和std :: end(),如果我“不容错不被允许”?

1 个答案:

答案 0 :(得分:4)

我首先报告说这对我来说就像一个gcc bug。现在看来,即使这部分for循环规范也不清楚,委员会已经开始进行调查。

看起来基于范围的for循环规则很快就会发生变化:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf

我不确定N3257中列出的选项会被选中。