1

这是一个非常短的片段,它不能用 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 tostd::basic_ostream >& operator<< (Foo&, char const (&) [4])' collect2: ld 返回 1 退出状态

任何人都可以解释为什么?

编辑

我也尝试过 clang 3.1,它说的完全一样。

4

2 回答 2

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使用类型实例化时,例如int[1] 中的朋友声明声明了一个模板函数:

template <typename U>
std::ostream& operator<<(Foo<int>&,U&);

但是该函数在任何地方都不存在,您在 [2] 中提供的是一个带有两个参数的模板:

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);

它声明了一个带有两个参数的模板(两者WU在这里未绑定),并在命名空间级别匹配您的定义。

另一种选择是在类模板定义中定义朋友函数,在这种情况下,您可以保留原始签名。有关不同替代方案的更多信息,请查看其他答案

于 2012-09-07T02:44:22.503 回答
1

您实际上并没有为 Foo 编写输出运算符<<

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

于 2012-09-07T02:36:55.587 回答