这是一个非常简短的片段,不能用g ++ 4.7.1编译(顺便说一下,它不能用gcc 4.6.3编译)。
#include <iostream>
template<typename T>
struct Foo
{
template<typename U>
friend std::ostream& operator<<(Foo&, U&);
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u)
{
std::cout << u;
return std::cout;
}
int main()
{
Foo<int> f;
f << "bar";
return 0;
}
这就是gcc 4.7.1输出(4.6.3几乎相同的东西)。
/tmp/ccNWJW6X.o:在函数
main': main.cpp:(.text+0x15): undefined reference to
std :: basic_ostream&gt;&amp; 运营商LT;&LT; (Foo&amp;,char const(&amp;)[4])'collect2: ld返回1退出状态
任何人都可以解释原因吗?
修改
我也尝试过使用clang 3.1,它说的完全一样。
答案 0 :(得分:8)
使用模板的友谊可能有点复杂......让我们看看你的代码做了什么:
template<typename T>
struct Foo {
template<typename U>
friend std::ostream& operator<<(Foo&, U&); // [1]
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u) { // [2]
std::cout << u;
return std::cout;
}
使用类型实例化Foo
时,例如[{1}},[1]中的友元声明声明了模板函数:
int
但是这个函数在任何地方都不存在,你在[2]中提供的是一个带有两个参数的模板:
template <typename U>
std::ostream& operator<<(Foo<int>&,U&);
关键是在实例化模板时处理friend声明,此时template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u);
表示使用当前实例化获得的类型。
您想要做的事情有不同的选择,最简单的方法是将朋友声明更改为:
Foo
其中声明了一个带有两个参数的模板(template<typename W, typename U>
friend std::ostream& operator<<(Foo<W> foo, U& u);
和W
都未绑定),并在命名空间级别匹配您的定义。
另一个选项是在类模板定义中定义友元函数,在这种情况下,您可以维护原始签名。有关不同备选方案的更多信息,请查看其他answer
答案 1 :(得分:1)
您实际上并未编写输出运算符&lt;&lt;为了Foo
请注意,这两个函数的签名非常不同