为什么模板函数不能编译?

时间:2012-09-07 02:28:28

标签: c++ templates gcc linker template-function

这是一个非常简短的片段,不能用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,它说的完全一样。

2 个答案:

答案 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

请注意,这两个函数的签名非常不同