Перегрузка istream_iterator ---- не может привязать lvalue к 'std:: basic_istream<char>&&'

Я сделал некоторые исследования, как:

Скажи мне, если я неправильно понял.

Я пытался ввести std:: pair из файла и хотел использовать std:: istream_iterator>

Вот код:

std::ifstream in("file.in");

std::map<int, int> pp; 
pp.insert((std::istream_iterator<std::pair<int, int> >(in)),
        std::istream_iterator<std::pair<int, int> >());

Я сделал перегрузку следующим образом:

std::istream & operator>>(std::istream & in, std::pair<int, int> & pa)
{ in >> pa.first >> pa.second; return in; }

std::istream & operator>>(std::istream && in, std::pair<int, int> & pa)
{ in >> pa.first >> pa.second; return in; }

Вот ошибка:

In file included from /usr/include/c++/4.8/iterator:66:0,
                 from No_12.cpp:7:
/usr/include/c++/4.8/bits/stream_iterator.h: In instantiation of ‘void std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::_M_read() [with _Tp = std::pair<int, int>; _CharT = char; _Traits = std::char_traits<char>; _Dist = long int]’:
/usr/include/c++/4.8/bits/stream_iterator.h:70:17:   required from ‘std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator(std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type&) [with _Tp = std::pair<int, int>; _CharT = char; _Traits = std::char_traits<char>; _Dist = long int; std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type = std::basic_istream<char>]’
No_12.cpp:23:59:   required from here
/usr/include/c++/4.8/bits/stream_iterator.h:121:17: error: cannot bind ‘std::istream_iterator<std::pair<int, int> >::istream_type {aka std::basic_istream<char>}’ lvalue to ‘std::basic_istream<char>&&’
      *_M_stream >> _M_value;
                 ^   
In file included from /usr/include/c++/4.8/iostream:40:0,
                 from /files/Nutstore/000/CPPWorkSpace/SHUCourse/DataStructures/BinarySearchTree/20130514/src/BinarySearchTree.hpp:6,
                 from /files/Nutstore/000/CPPWorkSpace/SHUCourse/DataStructures/BinarySearchTree/20130514/BinarySearchTree:3,
                 from No_12.cpp:1:
/usr/include/c++/4.8/istream:872:5: error:   initializing argument 1 of ‘std::basic_istream<_CharT, _Traits>& std::operator>>(std::basic_istream<_CharT, _Traits>&&, _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::pair<int, int>]’
     operator>>(basic_istream<_CharT, _Traits>&& __is, _Tp& __x)

Я не знаю, что я могу сделать сейчас... Надеюсь, что кто-то может дать мне совет. Благодарю.

1 ответ

Чтобы понять, в чем проблема, мы должны взглянуть на примерную реализацию istream_iterator (проблема возникает из требований Стандарта, которые неявно показаны здесь):

template < class T, class CharT, class Traits, class Distance >
class istream_iterator
{
    /* ... */
    typedef std::basic_istream<CharT, Traits>  istream_type;
    istream_type* stored_istream_ptr;
    T value;

public:
    /* ... */
    istream_iterator& operator++()
    {
        *stored_istream_ptr >> value; // (A)
        return *this;
    }
    T const& operator*() const
    {
        return value;
    }
    /* ... */
};

В строке (A) оператор применяется к объектам зависимых типов:

  • value имеет тип T
  • stored_istream_ptr имеет тип istream_typeсм. typedef

Согласно [temp.dep.type]/8, оба типа являются зависимыми.

Для выражения A >> B как и для любого другого вызова функции, сначала ищется имя функции (здесь: operator>>) -> поиск имени, тогда из набора найденных функций (перегрузок) выбирается и называется наиболее жизнеспособным -> разрешение перегрузки.

Для оператора ищутся как функции-члены, так и функции, не являющиеся членами (например, ваши операторы).

В этом случае задействованные типы являются зависимыми, поэтому применяются специальные правила для поиска имени [temp.dep.res]/1:

При разрешении зависимых имен учитываются имена из следующих источников:

  • Объявления, которые видны в точке определения шаблона.
  • Объявления из пространств имен, связанные с типами аргументов функции, как из контекста экземпляра (14.6.4.1), так и из контекста определения.

Ваши операторы были определены в глобальном пространстве имен, которое не связано ни с std::basic_istream ни std::pair, Следовательно, разрешение имен не находит ваших операторов, и разрешение перегрузки для выражения в строке (A) не выполняется.


Это объяснило бы сообщение об ошибке clang 3.2:

stream_iterator.h: 120: 17: ошибка: недопустимые операнды в двоичном выражении ('istream_type' (он же 'basic_istream ') и 'std::pair')

       *_M_stream >> _M_value;
       ~~~~~~~~~~ ^  ~~~~~~~~

И это объясняет, почему обходные пути работают.

С другой стороны, g++, похоже, показывает только одну перегрузку, обнаруженную разрешением имени, и причину, по которой он отклоняет ее (тогда как clang++ показывает все перегрузки, обнаруженные разрешением имени, с указанием причины, по которой она была отклонена). Одно из представлений g++ может быть "лучшим соответствием":

template< class CharT, class Traits, class T >
basic_istream<CharT,Traits>& operator>>(basic_istream<CharT,Traits>&&, T&);

Который, AFAIK, просто обертка для вызова другого operator>> в случае выражения istream_obj >> value работает на значение (как get_istream() >> value).

Это не связано с вашей проблемой (и сбивает с толку).


Два обходных пути:

  • использовать тип обертки, чтобы обернуть std::pair так что вы можете определить operator>> в пространстве имен, связанных с этим типом оболочки
  • введите ваши операторы в пространство имен std (не рекомендуется??)
Другие вопросы по тегам