Range-based для петель и ADL - PullRequest
6 голосов
/ 05 марта 2011

Стандартный рабочий проект C ++ 0x заявляет (раздел 6.5.4) о вызовах begin () и end (), которые неявны в цикле for на основе диапазона:

'begin' и 'end' ищутся с помощью аргумент-зависимого поиска (3.4.2).Для целей этого поиска имен пространство имен std является связанным пространством имен.

То, как я это читаю, означает, что разрешение перегрузки, установленное для вызовов begin () и end (), включает всеиз следующего:

  • все перегрузки begin () и end (), которые находятся в области действия в месте, где используется цикл for на основе диапазона (в частности, все перегрузки в глобальном пространстве имен будутнаходиться в области видимости)
  • все перегрузки begin () и end () в пространстве имен std
  • все перегрузки 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, а неперегрузки в глобальном пространстве имен.

Если, однако, я использую свой собственный класс пар, объявленный в глобальном пространстве имен, он прекрасно компилируется:

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 (), что, если я не ошибаюсьне допускается)?

1 Ответ

4 голосов
/ 05 марта 2011

Я впервые сообщил, что это выглядело как ошибка gcc для меня. Теперь кажется, что даже эта часть спецификации цикла for неясна, и в комитете было начато расследование.

Похоже, что правила для цикла for на основе диапазона изменятся очень скоро:

http://www.open -std.org / ОТК1 / SC22 / wg21 / документы / документы / 2011 / n3257.pdf

И я не уверен, какой вариант, указанный в N3257, будет выбран.

...