在考虑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
答案 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::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<<")";
}