c++ - 基于范围的 for 循环和 ADL

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

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

'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.

按照我的理解,这意味着为调用 begin() 和 end() 设置的重载决议包括以下所有内容:

  • 在使用基于范围的 for 循环的位置范围内的 begin() 和 end() 的所有重载(特别是,全局命名空间中的所有重载都将在范围内)
  • 命名空间 std 中 begin() 和 end() 的所有重载
  • 所有 begin() 和 end() 重载在与其参数关联的其他 namespace 中

对吗?

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

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

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

最佳答案

我首先报告说这对我来说看起来像一个 gcc 错误。现在看来,就连 for-loop 规范的这一部分也不清楚,并且已经向委员会公开了调查。

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

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

而且我不确定将选择 N3257 中列出的哪个选项。

关于c++ - 基于范围的 for 循环和 ADL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5200101/

相关文章:

javascript - 在 jQuery 中循环 JSON

java - java 中的foreach 构造创建的是硬拷贝还是软拷贝?

c++11 按值捕获 lambda 产生错误的值

c++ - 显式模板实例化

c++ - 'true' 和 'false' 在预处理器条件中是否具有通常的含义?

python - 需要哪个 $path 以便 g++/pybind11 可以找到 Python.h?

C++ 类 - 每 N 毫秒递增和递减属性

javascript - 如何从数组的 ForEach 循环中引用我的 TypeScript 类中的这个父方法?

c++ - 现代 CPU 中的多线程旧式应用程序

c++ - 在 SWIG 中获取 SFML 跨平台类型定义