Почему я не могу создать оператор << (ostream &, vector <T> &) с T = vector <int>?
В мыслях о вопросе итератора С++ я написал эту примерную программу:
#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/С++/4.4/bits/stream_iterator.h: 191: ошибка: нет соответствия для 'operator < < < in '((std:: ostream_iterator > , char, std:: char_traits > ) this) → std:: ostream_iterator > , char, std:: char_traits > :: _ M_stream < < __value
Почему это не удается? В моем шаблоне 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
Ответы
Ответ 1
Два слова: поиск имени.
Вот упрощенный пример того, что вы пытаетесь сделать, без каких-либо стандартных заголовков библиотеки:
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
.
Ответ 2
Поиск в контексте создания шаблона функции использует только 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
.
Ответ 3
Я думаю, так как 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