6

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

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • I'd suggest retagging it with `foreach`, since range-based-for-loop is just a more verbose term for it. – Collin Dauphinee Mar 04 '11 at 23:16
  • 1
    "does that mean it's impossible to treat an std::pair object as a range in a range-based for loop" - I suspect not, because even if you *had* misinterpreted the standard, C++0x now has partial specialization for function templates. You're not allowed to overload `std::begin`, but you are allowed to specialize it. – Steve Jessop Mar 05 '11 at 00:27
  • @Steve: Are you sure C++0x has partial specializations for function templates? I cannot find any mention of them in the draft. – HighCommander4 Mar 05 '11 at 01:29
  • @HighCommander4: define "sure". But no, I'm not, I'm working from vague memory. Since "class template partial specializations" rate a heading in n3225, and there's no such heading "function template partial specializations" I guess they didn't make it, so if GCC's implementation were correct then you'd be stuck. – Steve Jessop Mar 05 '11 at 01:47
  • @HighCommander4, @Steve: I could not find any mention of function template partial specialization either. However I think that gcc may be buggy here since it does not consider the current scope while regular look-up do. – Matthieu M. Mar 05 '11 at 11:24
  • @Steve it doesn't have partial specialization of function templates. And my guess is that it will never have them. They are (IMO) absolutely pointless because you can just overload them. – Johannes Schaub - litb Mar 05 '11 at 13:48
  • @litb: except in namespace `std`. But that's OK because the issue isn't really a lack of functionality, it's that the standard (probably rightly) thinks you shouldn't be allowed to change the behaviour of standard templated functions with standard classes. Because of gcc failing to find the overload in the global namespace, that was our last best hope for peace, but only because of a flaw in the compiler so not the standard's fault. – Steve Jessop Mar 06 '11 at 13:55
  • @litb: I agree, but the standard says that you can explicitly specialize functions in namespace std but not overload them. This results in an inconsistency - you can customize functions in namespace std in certain ways (i.e. for specific types) but not in others (i.e. for a set of types defined by a template). I think it's important that the standard allow you to customize functions in namespace std for a set of types defined by a template - whether it does that by allowing overloading of functions in namespace std, or by allowing partial specializations. – HighCommander4 Mar 06 '11 at 21:22
  • @HighCommander4: IIRC the way it's *supposed* to work is that when you define your template Foo, and you want to change the behaviour of, say, `sort(Foo::iterator, Foo::iterator)`, you can put a function template `sort` in the same namespace as `Foo::iterator`, and it should be found by ADL. Hence no need to overload or partially specialize `std::sort`. Your attempt to change the behavior of `begin` for `std::pair` is something that the standard thinks morally should be restricted to your own namespaces, *not* done in std. It's just GCC is blocking it, probably by accident. – Steve Jessop Mar 08 '11 at 10:51
  • 1
    However, very few people actually write `using std::sort; sort(foo.begin(), foo.end());`, in order to get your ADL-overloaded version of the algorithm. So generally clients will only get it if they know about it. You're right that therefore in practice there is an inconsistency, since if `Foo` was just a non-template class then we could sneak in a full specialization of `std::sort(Foo::iterator, Foo::iterator)`, and all users would get that even if they call fully-qualified `std::sort(foo.begin(), foo.end())`. – Steve Jessop Mar 08 '11 at 10:55

1 Answers1

4

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • I can see how GCC is doing this, because I find the description entirely confusing. I had the same question posted to usenet some time ago. See https://groups.google.com/group/comp.lang.c++/browse_thread/thread/7d14b0e6184daaea – Johannes Schaub - litb Mar 05 '11 at 13:44
  • Thanks Johannes. I've modified my answer accordingly. – Howard Hinnant Mar 05 '11 at 15:08
  • The problem with the specification is a good example of what happens when something is attempted to standardize before being widely used. I hope this get solved soon. – dsign May 25 '11 at 08:08