基于范围的 for 循环和 ADL

发布于 2024-10-20 12:54:24 字数 2983 浏览 8 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

◇流星雨 2024-10-27 12:54:24

我首先报告说这对我来说看起来像是一个 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文