Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Range-based for loops and ADL

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)?

like image 734
HighCommander4 Avatar asked Mar 04 '11 22:03

HighCommander4


1 Answers

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.

like image 153
Howard Hinnant Avatar answered Sep 24 '22 21:09

Howard Hinnant