为什么我不能用T = vector <int>实例化运算符&lt;&lt;(ostream&amp;,vector <t>&amp;)?</int> </t>

时间:2011-03-18 16:38:22

标签: c++ templates iostream

在考虑C++ iterator question时,我写了这个示例程序:

#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&lt;&lt;'不匹配((std :: ostream_iterator&gt;,char,std :: char_traits&gt; )this) - &gt; std :: ostream_iterator&gt;,char,std :: char_traits&gt; :: _ M_stream&lt;&lt; __value”

为什么这会失败?在我的模板operator<<中,我尝试指定任何向量,无论类型如何,都是可打印的。那么为什么不std::vector<std::vector<>>打印?

编辑:在模板功能中使用以下代码使其正常工作

#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 个答案:

答案 0 :(得分:16)

两个字:名字查找。

以下是您尝试执行的操作的简化示例,不需要任何标准库标头:

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命名空间中添加名称。

答案 1 :(得分:7)

在函数模板的实例化上下文中查找仅使用ADL。没有不合格的查询。因此,您需要依赖ADL。但vector<int>的ADL查找不包括全局命名空间,因此找不到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
}

这是因为全局命名空间与std::vector<A>的ADL查找集相关联(因为A是模板参数),因此在实例化{{{{1}的相应成员函数时查找全局声明的模板。 1}},它会使用std::ostream_iterator<> operator<<T,然后A使用operator<<(int) {/ 1}}。

答案 2 :(得分:2)

我认为,因为std::copy是在不同的命名空间中定义的,并且来自函数模板std::copyostream_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