【问题标题】:Range-based for loops and ADL基于范围的 for 循环和 ADL
【发布时间】:2011-03-04 22:43:39
【问题描述】:

C++0x standard working draft 声明(第 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'

所以它似乎在考虑 only 命名空间 std 和其他相关命名空间中的重载,而不是在调用站点范围内的重载(我上面列表中的第一个要点)。

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

如果是后者,这是否意味着不可能将 std::pair 对象视为基于范围的 for 循环中的范围(不重载 std::begin() 和 std::end(),如果我'm not wrong is not allowed)?

【问题讨论】:

  • 我建议用foreach 重新标记它,因为基于范围的for-loop 只是一个更冗长的术语。
  • “这是否意味着不可能将 std::pair 对象视为基于范围的 for 循环中的范围” - 我怀疑不是,因为即使你 误解了标准,C++0x 现在对函数模板进行了部分专门化。你不能重载std::begin,但你可以专门化它。
  • @Steve:你确定 C++0x 对函数模板有部分特化吗?我在草稿中找不到任何提及。
  • @HighCommander4:定义“确定”。但不,我不是,我是在模糊的记忆中工作。由于“类模板部分专业化”对 n3225 中的标题进行评分,并且没有这样的标题“功能模板部分专业化”我猜他们没有做到,所以如果 GCC 的实现是正确的,那么你会被卡住。
  • 然而,很少有人真正写using std::sort; sort(foo.begin(), foo.end());,以便获得您的算法的ADL重载版本。因此,通常客户只有在知道它的情况下才会得到它。你是对的,因此在实践中存在不一致,因为如果Foo 只是一个非模板类,那么我们可以潜入std::sort(Foo::iterator, Foo::iterator) 的完全专业化,即使他们完全调用所有用户也会得到它 -合格的std::sort(foo.begin(), foo.end())

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


【解决方案1】:

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

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

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

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

【讨论】:

  • 我可以看到 GCC 是如何做到这一点的,因为我发现描述完全令人困惑。前段时间我在usenet上发布了同样的问题。见groups.google.com/group/comp.lang.c++/browse_thread/thread/…
  • 谢谢约翰内斯。我已经相应地修改了我的答案。
  • 规范的问题是一个很好的例子,说明当某事物在被广泛使用之前试图标准化时会发生什么。我希望这个问题能尽快解决。
猜你喜欢
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
  • 2014-01-12
  • 2013-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多