Why can't I instantiate operator<<(ostream&, vector<T>&) with T=vector<int>?
In thinking about C++ iterator question, I wrote this sample program:
#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
}
I compile this program with gcc and get the typical 100 lines of errors. The relevant part, I believe, is:
it.cc:19: instantiated from here
/usr/include/c++/4.4/bits/stream_iterator.h:191: error: no match for ‘operator<<’ in ‘((std::ostream_iterator >, char, std::char_traits >)this)->std::ostream_iterator >, char, std::char_traits >::_M_stream << __value’
Why does this fail? In my templated operator<<
, I try to specify that any vector, regardless of type, is printable. So why doesn't std::vector<std::vector<>>
print?
EDIT: Using the following code in the template function makes it work
#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
Two words: name lookup.
Here is a simplified example of what you are trying to do, without any Standard Library headers required:
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
}
In this example, in main()
, the only f
that is found during normal name lookup is the function template in the global namespace, and it matches, so main
uses it (ns::f
is also found during argument-dependent lookup, but it isn't a match so the global f
is still selected during overload resolution).
In test
, however, the ns::f(int)
overload is found and name lookup stops. Namespaces are searched outwards, so ns
is searched first, then the global namespace, but name lookup stops once a name is found, so once ns::f(int)
is found, name lookup stops. Argument-dependent lookup also takes place and also finds ns::f(int)
, since C
is in namespace ns
, then ADL stops searching.
The same is true in your example: in main()
, the operator<<
overload is found, but inside of the std::ostream_iterator
, which is in the std
namespace, other <<
overloads are found, and so your overload is not found.
Your operator<<
overload would need to be in the std
namespace for it to work, but unfortunately you aren't allowed to add names to the std
namespace.
Lookup in the instantiation context of a function template only uses ADL. No unqualified lookup. Therefor, you need to rely on ADL. But the ADL lookup for vector<int>
does not include the global namespace, hence your operator<<
is not found. Try this, which should work fine:
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
}
This works because the global namespace is associated with the ADL lookup set for std::vector<A>
(because A
is a template argument), therefor finding the globally declared template when instantiating the respective member functions of std::ostream_iterator<>
, which will use your operator<<
for T
being A
, which then in turn will use operator<<(int)
of std::ostream
.
I think since std::copy
is defined in different namespace, and the generated code from the function template std::copy
and ostream_iterator
class template, is unable to find the operator<<
defined by you which exists in different namespace.
To solve this problem you've to define operator<<
in std
namespace as shown below:
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<<")";
}
}
Working code at ideone : http://ideone.com/sFenn
However, I cannot say how good the idea of implementing it is in std
namespace!
Alternatively, you can define operator<<
as (without using 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<<")";
}
Working code : http://ideone.com/FXWlP
精彩评论