2010-04-29 144 views
10

看到下面的代码,请清除我的疑惑。显式模板实例如何影响链接器可以找到的内容?

  1. 由于ABC是一个模板,它为什么不显示一个错误,当我们把TEST.CPP的ABC类的成员函数的定义是什么?

  2. 如果我把test.cpp代码放在test.h和remve 2中,那么它工作正常。为什么?

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 
+0

你为什么要问?他们是两个毫不相关的陈述。 – 2010-04-29 07:29:14

+0

@Dennis Zickefoose:大家都是初学者在某些点 – ereOn 2010-04-29 07:33:24

+0

@ereOn:是的,但为什么他问能走很长的路向建议适当的解决办法回答他的问题时采取。 – 2010-04-29 07:57:27

回答

13

在这两种情况下,你都在做一个明确的实例化。在第二种情况下,只有ABC<char>::foo正在实例化,而在第一种情况下,ABC<char>::bar也正在实例化。

不同的类似的例子可以澄清的含义:

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 

在该示例中,在main编译器不能看到foo也不bar定义,因此它不能实例化的方法。编译器在处理main.cpp时会很乐意接受主代码,因为您告诉它是一个模板,并且它具有这两个函数,并且会假定它们将在其他某个翻译单元中定义。

在包含test.cpp的翻译单元中,编译器看到两个方法定义,并且可以完全处理两个实例(方法/类)。如果仅存在方法实例化([1]),则编译器将仅生成该方法,并且会使bar未定义。因此,包含test.h的任何代码都会与编译的test.cpp链接,并且只使用foo方法进行编译和链接,但bar的使用将由于未定义而无法链接。

显式实例化类模板为所有成员方法生成符号,在这种情况下,包含test.h和任何包含编译的test.cpp对象文件链接的翻译单元都将编译和链接。

+0

我有两个疑惑 1.为ABC是模板,为什么它没有显示错误,当我们把ABC类的成员函数的界定及在TEST.CPP 2.如果我在test.h把TEST.CPP代码并删除2,那么它的工作好的,请你解释一下 – Suri 2010-04-29 08:47:24

+0

模板方法定义不需要在类体内部,即使是推荐的。与常规函数一样,如果编译器只能看到方法声明,它会假定它是在其他地方定义的,如cpp中的示例所示。这种方法存在问题,因为您正在将可用于模板的类型限制为您为其提供明确专业化的类型。这可以用于加快编译时间,对于那些预先知道实例化模板的所有类型的模板化代码。 – 2010-04-29 09:50:16

+0

第二个问题,如果你把所有的代码放在一起,编译器会看到模板定义(方法),所以当你在main中调用'bar'方法时,它会执行该方法的隐式实例化。请注意,只有在编译器不能隐式实例化模板的情况下才需要显式实例化,并且在大多数情况下,如果编译器有足够的信息,通过允许它决定实例化哪些方法(仅使用那些方法),您将提高编译时间(和中间值目标文件,即使在大多数情况下,连接器可以丢弃未使用的符号) – 2010-04-29 09:53:19

0

(这是我原来的答复,由大卫·罗德里格斯的观察提示的编辑版本。)

#1实例化类模板,并作为该部分实例的所有方法。

#2实例化类的一个成员方法。作为它的一部分,它必须实例化类模板,但不是所有其他方法。

如果您在bar()中引入了一个依赖于类型的错误(例如,像void *x = b;这样的语句),则可以看到差异。您将收到#1的编译器错误,但不会与#2。另请注意,编译器(gcc至少)不会编译#1,然后是#2,但会编译其中任何一个,或者如果#2后跟#1

+0

当实例化类模板时,只有实际_used_成员被实例化。这就是为什么我相信你#2是错误的。大卫的回答也是如此。 – sbi 2010-04-29 07:51:21

0

我想你想{}而不是;在#1。

相关问题