【发布时间】:2014-04-02 13:25:26
【问题描述】:
看看下面的例子:
啊哈:
class A {
protected:
template<class T>
friend void b();
};
b.h:
template<class T>
void b() {}
main.cxx:
#include "a.h"
int main()
{
b<double>();
}
请注意,我忘记在主文件中包含“b.h”。不幸的是,编译器选择了类 A 中的朋友声明并编译了主函数而没有抱怨。我留下了一个神秘的链接错误,这令人惊讶,因为实例化应该发生在 main.cxx 中:
[joel@fedora-joel friend]$ g++ main.cxx
/tmp/ccfpaeHw.o: In function `main':
main.cxx:(.text+0x5): undefined reference to `void b<double>()'
collect2: error: ld returned 1 exit status
我的问题是:为什么编译器允许这样做?有没有办法对付它?
【问题讨论】:
-
根据单独编译的原则,只要一个TU所需的所有信息在编译时可用,每个翻译单元(TU)都会单独编译。对于带有 b 签名的函数,它肯定是(在预编译器将 a.h 传递到 main.cpp 之后)。未解析的引用被输入到符号表中,由链接器解析(因此出现链接器错误)。我不知道如何(或为什么)避免这种情况。
-
为什么要避免这种情况?因为丑!恕我直言,将函数的存在作为类的朋友提及不应允许外部代码使用它。我确信这种行为有很好的技术原因,但是找到一种方法使该声明对外部代码不可用可以避免错误,尤其是对于初学者。
-
“为什么”更多是技术问题。如果您通过编译器/链接器所采取的步骤,这将很难实现(例如,您需要一些跨 TU 有效性检查)。 C(++) 传统上使用“单独编译”,这在当时是不可能的。是的,我同意,这可能会在调试时导致难以跟踪的错误。
-
这就是为什么你不应该向在不同标题中定义的东西声明友谊。
protected:在这里也毫无意义;朋友声明没有访问说明符。