问题
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
回答1:
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.
回答2:
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
.
回答3:
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
来源:https://stackoverflow.com/questions/5355081/why-cant-i-instantiate-operatorostream-vectort-with-t-vectorint