C++ 基于范围的环路和ADL

C++ 基于范围的环路和ADL,c++,foreach,c++11,argument-dependent-lookup,C++,Foreach,C++11,Argument Dependent Lookup,对于基于范围的for循环中隐含的begin()和end()调用,状态(第6.5.4节)如下所示: “开始”和“结束”是用 参数相关查找(3.4.2)。对于 此名称查找的目的, 命名空间std是一个关联的 命名空间 按照我的理解,这意味着为begin()和end()调用设置的重载解析包括以下所有内容: 在使用基于范围的for循环的位置的作用域中的begin()和end()的所有重载(特别是,全局命名空间中的所有重载都将在作用域中) 命名空间std中begin()和end()的所有重载 其他名称空

对于基于范围的for循环中隐含的begin()和end()调用,状态(第6.5.4节)如下所示:

“开始”和“结束”是用 参数相关查找(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)
        ;
}
因此,它似乎只考虑了名称空间std和其他相关名称空间中的重载,而不是调用站点范围内的重载(我上面列表中的第一个要点)

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


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

我首先报告说,这看起来像是一个gcc错误。现在看来,甚至for-loop规范的这一部分也不清楚,委员会已经开始进行调查

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


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

我建议使用
foreach
重新标记它,因为基于范围的for循环只是一个更为详细的术语。“这是否意味着不可能将std::pair对象视为基于范围的for循环中的范围”-我怀疑不是,因为即使您误解了该标准,C++0x现在对函数模板进行了部分专门化。不允许重载
std::begin
,但允许对其进行专门化。@史蒂夫:确定C++0x对函数模板进行了部分专门化吗?我在草稿中找不到任何提到它们的地方。@HighCommander4:定义“确定”。但不,我不是,我是根据模糊的记忆工作的。由于“类模板部分专业化”在n3225中是一个标题,而没有“函数模板部分专业化”这样的标题,我猜他们没有做到,所以如果GCC的实现是正确的,那么你就会陷入困境;排序(foo.begin(),foo.end()),以获取算法的ADL重载版本。因此,一般来说,客户只有在知道它的情况下才会得到它。你是对的,因此在实践中存在不一致性,因为如果
Foo
只是一个非模板类,那么我们可以偷偷地使用
std::sort(Foo::iterator,Foo::iterator)
,并且所有用户都会得到,即使他们调用完全限定的
std::sort(Foo.begin(),Foo.end())
。我可以看到GCC是如何做到这一点的,因为我发现描述完全令人困惑。不久前,我在usenet上发布了同样的问题。谢谢你,约翰。我已经相应地修改了我的答案。规范中的问题是一个很好的例子,说明了在广泛使用之前尝试标准化某些东西时会发生什么。我希望这个问题能尽快解决。
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>)
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)
        ;
}
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'