Почему я не могу создать экземпляр оператора << (ostream &, vector <T>&) с T = vector <int>? - PullRequest
13 голосов
/ 18 марта 2011

Размышляя о вопросе итератора C ++ , я написал этот пример программы:

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm> 

template <class T>
std::ostream& operator<<(std::ostream&os, const std::vector<T>& v) 
{ 
    os<<"(";
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(os, ", "));
    return os<<")";
}

int main()
{
    std::vector<int> v(3);
    std::vector<std::vector<int> > vv(3, v);
    std::cout << v << "\n"; // this line works
    std::cout << vv << "\n"; // this line produces error
}

Я компилирую эту программу с помощью gcc и получаю типичные 100 строк ошибок.Соответствующая часть, я полагаю, такова:

it.cc: 19: создается отсюда

/ usr / include / c ++ / 4.4 / bits / stream_iterator.h: 191:ошибка: нет совпадения для 'operator <<' в '<em> ((std :: ostream_iterator>, char, std :: char_traits> ) this) -> std :: ostream_iterator>, char, std :: char_traits> :: _ M_stream << __value '</p>

Почему это не получается?В моем шаблонном operator<< я пытаюсь указать, что любой вектор , независимо от типа, пригоден для печати.Так почему же std::vector<std::vector<>> не печатает?

EDIT : использование следующего кода в функции шаблона делает его работоспособным

#if 0
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(os, ", "));
#else
    for(typename std::vector<T>::const_iterator it = v.begin();
        it != v.end();
        it++) {
        os<<(*it)<<", ";
    }
#endif

Ответы [ 3 ]

16 голосов
/ 18 марта 2011

Два слова: поиск имени.

Вот упрощенный пример того, что вы пытаетесь сделать, без каких-либо заголовков стандартной библиотеки:

template <typename T> void f(T) { }

namespace ns {
    class C { };

    void f(int) { }

    void test() { f(C()); } // doesn't work :'(
}

int main() {
    f(ns::C());             // works!  :-D
}

В этом примере в main() только f, которыйво время обычного поиска имени является шаблоном функции в глобальном пространстве имен, и он совпадает, поэтому main использует его (ns::f также найдено во время зависимого от аргумента поиска, но это не совпадение, поэтому глобальный f все еще выбирается во время разрешения перегрузки).

В test, однако, перегрузка ns::f(int) обнаружена, и поиск имени останавливается.Пространства имен ищутся вовне, поэтому сначала ищется ns, затем - глобальное пространство имен, но поиск имени останавливается после того, как имя найдено, поэтому, когда ns::f(int) найдено, поиск имени останавливается.Зависимый от аргумента поиск также имеет место и находит ns::f(int), поскольку C находится в пространстве имен ns, тогда ADL прекращает поиск.

То же самое верно в вашем примере: в main()Перегрузка operator<< обнаружена, но внутри std::ostream_iterator, который находится в пространстве имен std, обнаружены другие перегрузки <<, поэтому ваша перегрузка не найдена.

Ваш operator<<Перегрузка должна быть в пространстве имен std, чтобы она работала, но, к сожалению, вы не можете добавлять имена в пространство имен std.

7 голосов
/ 18 марта 2011

Поиск в контексте реализации шаблона функции использует только ADL.Нет безоговорочного поиска.Поэтому вам нужно положиться на ADL.Но поиск ADL для vector<int> не включает в себя глобальное пространство имен, поэтому ваш operator<< не найден.Попробуйте это, что должно работать нормально:

struct A { 
  operator int() const { return 0; }
};

template <class T>
std::ostream& operator<<(std::ostream&os, const std::vector<T>& v) 
{ 
    os<<"(";
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(os, ", "));
    return os<<")";
}

int main()
{
    std::vector<A> v(3);
    std::vector< std::vector<A> > vv(3, v);
    std::cout << vv << "\n"; // should work fine
}

Это работает, потому что глобальное пространство имен связано с поиском ADL, установленным для std::vector<A> (потому что A является аргументом шаблона), что позволяет найти глобально объявленныйшаблон при создании соответствующих функций-членов std::ostream_iterator<>, который будет использовать ваш operator<< для T, равный A, который в свою очередь будет использовать operator<<(int) из std::ostream.

2 голосов
/ 18 марта 2011

Я думаю, что поскольку std::copy определено в другом пространстве имен, а сгенерированный код из шаблона класса std::copy и ostream_iterator не может найти определенный вами operator<<, который существует в другом пространстве имен.

Чтобы решить эту проблему, вы должны определить operator<< в std пространстве имен, как показано ниже:

namespace std //note the namespace
{
   template <class T>
   std::ostream& operator<<(std::ostream&os, const std::vector<T>& v) 
   { 
     os<<"(";
      std::copy(v.begin(), v.end(), std::ostream_iterator<T>(os, ", "));
      return os<<")"; 
   }
}

Рабочий код на ideone: http://ideone.com/sFenn

Однако я не могу сказать, насколько хороша идея его реализации в std пространстве имен!


В качестве альтернативы вы можете определить operator<< как ( без использования std::copy):

template <class T>
std::ostream& operator<<(std::ostream&os, const std::vector<T>& v) 
{ 
   typedef typename std::vector<T>::const_iterator const_iterator;
   os<<"(";
   for (const_iterator it = v.begin() ; it != v.end() ; ++it )
       os << *it << ", ";
   return os<<")"; 
}

Рабочий код: http://ideone.com/FXWlP

...