Why can't I instantiate operator<<(ostream&, vector<T>&) with T=vector<int>?

廉价感情. 提交于 2019-11-29 07:14:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!