Почему range-for не находит мои перегрузки начала и конца для std::istream_iterator?
У меня есть такой код
std::ifstream file(filename, std::ios_base::in);
if(file.good())
{
file.imbue(std::locale(std::locale(), new delimeter_tokens()));
for(auto& entry : std::istream_iterator<std::string>(file))
{
std::cout << entry << std::endl;
}
}
file.close();
где std::istream_iterator<std::string>
"s begin()
а также end()
определяются следующим образом
template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T>& stream)
{
return stream;
}
template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>& stream)
{
return std::istream_iterator<T>();
}
это то, о чем Марк Нельсон также написал в книге доктора Добба. Увы, код не может скомпилироваться в моей Visual Studio 2012 с сообщениями об ошибках
ошибка C3312: не найдена вызываемая функция 'begin' для типа 'std:: istream_iterator <_Ty>'
а также
ошибка C3312: не найдена вызываемая функция 'end' для типа 'std:: istream_iterator <_Ty>'
Вопрос: Есть ли что-то, чего я не заметил, ошибка в компиляторе (маловероятно, но на всякий случай) или... Ну, есть идеи?
Этот вопрос значительно Xeo, как рекомендует Xeo. Чтобы предоставить больше информации и ссылок, это связано с моим другим вопросом о Stackru, мне было интересно, как сделать синтаксический анализ строк более чистым, чем обычные циклы. Немного кодирования и проверки из интернета, и у меня был рабочий набросок следующим образом
std::ifstream file(filename, std::ios_base::in);
if(file.good())
{
file.imbue(std::locale(std::locale(), new delimeter_tokens()));
for(auto& entry : istream_range<std::string>(file)
{
std::cout << entry << std::endl;
}
}
file.close();
но была небольшая загвоздка, которую я пытался исправить. Я думаю, что это будет выглядеть более естественно, как в коде, который не компилируется и не нравится
for(auto& entry : istream_range<std::string>(file)
Пожалуйста, обратите внимание на другой итератор. Delimeter_tokens определяется так, как любезно показал Nawaz здесь (код не продублирован) и istream_range, как в блоге Code Synthesis здесь. Я думаю, что начало и конец реализации должны работать, как рекламируется в вышеупомянутом сообщении в блоге Синтез кода
Последнее правило (откат к автономным функциям begin() и end()) позволяет нам неинвазивно адаптировать существующий контейнер к основанному на диапазоне интерфейсу цикла.
Таким образом, мой вопрос со всей (ир) соответствующей историей.
2 ответа
Ranged-for полагается на ADL, если специальная обработка для собственного массива (T foo[N]
) и член begin
/ end
не дает никаких результатов.
§6.5.4 [stmt.ranged] p1
- в противном случае begin-expr и end-expr
begin(__range)
а такжеend(__range)
соответственно гдеbegin
а такжеend
ищутся с аргумент-зависимым поиском (3.4.2). В целях поиска этого имени, пространство именstd
это связанное пространство имен.
Ваша проблема в том, что связанное пространство имен std::istream_iterator
есть (очевидно) namespace std
, а не глобальное пространство имен.
§3.4.2 [basic.lookup.argdep] p2
Для каждого типа аргумента
T
в вызове функции есть набор из нуля или более связанных пространств имен и набор из нуля или более связанных классов, которые необходимо рассмотреть. Наборы пространств имен и классов полностью определяются типами аргументов функции [...].
- Если
T
является фундаментальным типом, его связанные наборы пространств имен и классов являются пустыми.- Если
T
является типом класса (включая объединения), его ассоциированными классами являются: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Его связанные пространства имен - это пространства имен, членами которых являются связанные классы. Кроме того, если T является специализацией шаблона класса, его связанные пространства имен и классы также включают: пространства имен и классы, связанные с типами аргументов шаблона, предоставленных для параметров типа шаблона [...].
Обратите внимание на последнюю (цитируемую) часть второго маркера. В основном это означает, что использование класса, который является членом глобального пространства имен в качестве аргумента шаблона, заставляет код работать:
#include <iterator>
#include <iostream>
template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T> is){
return is;
}
template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>){
return std::istream_iterator<T>();
}
struct foo{};
std::istream& operator>>(std::istream& is, foo){
return is;
}
int main(){
for(foo f : std::istream_iterator<foo>(std::cin))
// ^^^
// make global namespace one of the associated namespaces
;
}
Из-за поиска, зависящего от аргумента, компилятор пытается найти begin()
а также end()
в std
Пространство имен. Если вы поместите свои функции туда, код скомпилируется.
Так как поиск имени - сложная проблема в C++, я не совсем уверен, правильно ли ведет себя компилятор.