LNK2019 - 为什么使用模板友元函数会出现未解析的外部符号错误

3
我不明白这个错误的原因:
#include <iostream>
using namespace std;

// LNK2019f.cpp
// LNK2019 expected
template<class T>
void f(T) {}

template<class T>
struct S {
   friend void f(T);
   // try the folowing line instead
   // friend void f<T>(T);
};

int main() {
   S<int> s;
   int a = 2;
   f(a);   // unresolved external
}

源自http://msdn.microsoft.com/en-us/library/799kze2z(v=vs.80).aspx

如果我将S<int> s注释掉,为什么错误就不会显示呢?我知道需要声明模板参数列表,但我看不出那个带有模板的结构与f(a)调用之间的联系。

另一个奇怪的事情是:如果我只注释掉f(a)的调用(并保留所有其余内容),它会再次编译。我使用的是MSVC2012。

2个回答

2
错误出现的原因是您的友元声明作为另一个非模板函数的函数声明。您需要像这样声明它,以便告诉编译器它是一个模板函数:
    friend void f<T>(T);

考虑以下示例:
template<class T>
struct S {
    friend void foo(int);
};

int main() {
    S<int> s;
    foo(42);
}

这会抛出一个链接错误,指出一个未解析的外部符号foo。在这种情况下,foo被声明但通过友元声明未定义。

如果我们现在注释掉 S<int> s;,我们得到的不是链接器错误,而是编译器错误:'foo':找不到标识符,因为S<int>没有被编译,所以foo没有被声明。


@DavidKernin 我猜测你的友元声明充当了另一个函数 f 的函数声明,这个函数隐藏了你的模板函数。顺便说一下,我观察到 typename 实际上并不是必要的。我会检查并相应地进行编辑。 - Sebastian Hoffmann
@DavidKernin 是的,那就是原因,我会更新我的帖子。 - Sebastian Hoffmann
我猜MSVC出了点问题... http://ideone.com/WldN4E 在任何情况下都不会失败。而且似乎更合乎逻辑,你认为呢Paranaix? - Marco A.
@DavidKernin 你可能是对的,因为我在标准中找到了这段文字:“未经限定查找(3.4.1)或限定查找(3.4.3)均无法找到友元的名称,直到该命名空间范围内提供了匹配的声明(在类定义之前或之后授予友元)。 ”因此,虽然友元声明实际上声明了一个函数,但在封闭命名空间中声明该名称时不会考虑已声明的名称。 因此,编译器应该能够找到模板化的f函数。 - Sebastian Hoffmann
MSVC从来没有很好地尊重标准...但对我来说已经足够了。我会授予您积分,但请编辑您的答案并附上最近的发现,以便其他人也能受益。谢谢! - Marco A.
显示剩余2条评论

0

如上所述,友元函数f(T)需要成为模板函数。编译器期望在main函数中实例化f(T)。


1
编译器应该能够推断类型。 - Sebastian Hoffmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接