2012-07-01 5 views
10

У меня есть подобный кодПочему не подходит для поиска моих перегрузок начала и конца для 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>(); 
} 

, который является тем, что Марк Нельсон также писал о в р Добба here. Увы, коду не удается скомпилировать в моем Visual Studio 2012 с сообщениями об ошибках

ошибка C3312: не вызываемой функции 'начать' найден для типа 'станд :: istream_iterator < _Ty>'

и

ошибка C3312: нет вызываемой функции 'конца' найден для типа 'станд :: istream_iterator < _Ty>'

Вопрос: Есть ли что-то, чего я не заметил, ошибка в компиляторе (маловероятно, но на всякий случай) или ... Ну, какие-нибудь идеи?


Эти вопросы тщательно очищаются, как сообщается Xeo. Чтобы предоставить больше информации и ссылок, это связано с моим other question на Stackoverflow, мне было интересно, как сделать синтаксический анализ на основе строк, чем обычные циклы. Немного кодирования и проверки из Интернета, и я имел рабочий эскиз следующим

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 любезно показал here (код не дублируется) и istream_range как в код синтеза блога here. Я думаю, что начинающие и конечные реализации должны работать, как описано в вышеупомянутом сообщении блога Code Synthesis

Последнее правило (возврат к автономным функциям begin() и end()) позволяет нам неинвазивно адаптировать существующий контейнер для интерфейса на основе диапазона.

Таким образом, мой вопрос со всем (ir) соответствующим фоном.

+0

Я взял на себя смелость изменить заголовок вопроса, чтобы отразить фактический заданный вопрос. Откат или изменение при необходимости. – Xeo

+0

Как мне кажется, это так. Он привлекает внимание тех, кто ищет решение, подобное моему, и тех, у кого есть проблема поиска. Как я могу сделать ваш пост ответом (или кто-то еще сделал это)? – Veksi

+0

Спасибо за это! : D – Veksi

ответ

7

Ranged-for полагается на ADL, если специальная обработка для собственного массива (T foo[N]) и члена begin/end не дает никаких результатов.

§6.5.4 [stmt.ranged] p1

  • в противном случае, начинают выраж- и торцевых выраж являются 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 
    ; 
} 
+0

Я не могу последовать за тобой сейчас. Как этот трюк ассоциации пространства имен будет работать, если я должен прочитать токенизированные строки из потока файлов? То есть, вы указываете, что в цикле вы должны указывать строки в моем случае. – Veksi

+0

@Veksi: В принципе, важна только первая часть моего ответа: вы не можете перегружать элементы 'std' в глобальном пространстве имен для диапазона. Вторая часть заключалась только в том, чтобы продемонстрировать причудливость ADL, которая позволила бы этому работать. : P – Xeo

+1

Комментируя [этот вопрос и ответы] (http://stackoverflow.com/a/18321288/819272), я узнал о [* основной проблеме * 1442] (http://wg21.cmeerw.net/cwg/ issue1442), который немного изменяет правила. Пространство имен 'std' больше не является ассоциированным пространством имен и [* явно указано, что *] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3691.pdf)" начинаются , а конец просматривается в связанных пространствах имен (3.4.2). [Примечание: Обычный неквалифицированный поиск (3.4.1) не выполняется. - примечание конца) " – TemplateRex

1

Из-за аргумента зависящего поиска компилятор пытается найти begin() и end() в пространстве имен std. Если вы разместите свои функции, код компилируется.

Поскольку поиск имени является сложной проблемой в C++, я не совсем уверен, правильно ли работает компилятор или нет.

+1

Вам не разрешено помещать перегрузки в 'namespace std'. – Xeo

+0

Но вы можете специализировать шаблоны, нет? – jrok

+2

@jrok: Да, но только для ваших пользовательских типов, а не для типов, которые исходят из 'std' (IIRC). Кроме того, вы не можете * частично * специализировать шаблоны функций, а специализация шаблонов функций в целом неодобрительно. – Xeo