为什么模板类函数必须在同一个翻译单元中声明?
以这段代码为例
/*
* foo.h
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#ifndef FOO_H_
#define FOO_H_
template<typename Type>
class Foo
{
public:
void Bar(Type object);
};
#endif /* FOO_H_ */
:
/*
* foo.cpp
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#include <iostream>
using namespace std;
#include "foo.h"
template<typename Type>
void Foo<Type>::Bar(Type object)
{
cout << object;
}
。
/*
* main.cpp
*
* Created on: Oct 15, 2011
* Author: AutoBotAM
*/
#include <iostream>
using namespace std;
#include "foo.h"
Foo<int> foo;
int main()
{
cout << "The year is ";
foo.Bar(2011);
return 0;
}
这就是我通常声明非模板类的方式。不幸的是,此代码会导致错误 ../src/main.cpp:18: undefined reference to 'Foo
(在 MinGW 中)。我做了一些阅读,结果发现你必须在同一个翻译单元中声明模板类,如下所示:
/*
* foo.h
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#ifndef FOO_H_
#define FOO_H_
template<typename Type>
class Foo
{
public:
void Bar(Type object)
{
cout << object;
}
};
#endif /* FOO_H_ */
我的大问题是,为什么你必须这样做?我可以想象这个方案在开发过程中会遇到一些陷阱。例如,假设我们有 50 个翻译单元#包括 foo.h
,并且我们对 void Foo::Bar(Type)
进行了更改。由于 Bar
位于头文件中,因此我们必须等待所有 50 个翻译单元编译完毕才能获得任何结果。如果我们让 Bar
单独在 foo.cpp
中工作,我们只需等待 1 个翻译单元编译即可。有什么方法可以克服这个问题吗?
感谢您的任何建议!
Take this code, for example:
/*
* foo.h
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#ifndef FOO_H_
#define FOO_H_
template<typename Type>
class Foo
{
public:
void Bar(Type object);
};
#endif /* FOO_H_ */
.
/*
* foo.cpp
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#include <iostream>
using namespace std;
#include "foo.h"
template<typename Type>
void Foo<Type>::Bar(Type object)
{
cout << object;
}
.
/*
* main.cpp
*
* Created on: Oct 15, 2011
* Author: AutoBotAM
*/
#include <iostream>
using namespace std;
#include "foo.h"
Foo<int> foo;
int main()
{
cout << "The year is ";
foo.Bar(2011);
return 0;
}
This is how I usually go about declaring non-template classes. Unfortunately this code results in the error ../src/main.cpp:18: undefined reference to 'Foo<int>::Bar(int)'
(in MinGW). I did some reading, and it turns out you have to declare template classes in the same translation unit, like so:
/*
* foo.h
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#ifndef FOO_H_
#define FOO_H_
template<typename Type>
class Foo
{
public:
void Bar(Type object)
{
cout << object;
}
};
#endif /* FOO_H_ */
My big question is, why do you have to do this? I could imagine a few pitfalls in development with this scheme. For example, imagine we had 50 translation units #including foo.h
, and we make a change to void Foo::Bar(Type)
. Since Bar
is in the header file, we'll have to wait for all 50 of those translation units to compile before we get any results. If we had Bar
working in foo.cpp
separately, we would only have to wait for 1 translation unit to compile. Are there any ways to overcome this issue?
Thanks for any advice!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
模板位于编译和链接时间之间。在看到模板声明后,急切的实现可以做很多事情,但在使用模板参数实例化模板之前无法生成实际代码。
您可以在 cpp 文件中包含模板类函数,并且可以使用将要使用它的参数显式实例化它。但是,您只能在整个程序中使用这些实例化。例如,您
现在可以添加到 foo.cpp 中,即使实现位于其自己的翻译单元中,您也可以在任何地方使用
Foo
。但是,您不能使用任何其他类型的Foo<>
,因为链接器将无法找到其函数(它们实际上并不存在)。Templates lay somewhere between compile and link time. An eager implementation can do much after seeing a template declaration, but actual code cannot be generated until the template is instantiated with its template arguments.
You can have template class functions in a cpp file, and you can explicitly instantiate it with those arguments that you will be using it. However, you can only use those instantiations in your entire program. For instance, you can add to foo.cpp
now you can use
Foo<int>
s anywhere even while the implementations are in a translation unit of its own. However you cannot use any other kind ofFoo<>
since the linker won't be able to find its functions (they don't actually exist).模板不是类型。它们只是模板。它们只有在被实例化(具有完整的参数集)时才成为一种类型。
编译要求所有必要的类型定义在编译时可用。此外,链接要求所有必需的函数定义(包括成员函数)存在于某个翻译单元中(内联提供了对单一定义规则的通常豁免)。
如果将所有这些放在一起,几乎可以自动得出以下结论:模板类的所有模板成员函数定义都必须可用于编译过程中在某个时刻的每个使用的模板实例化。
另一方面,请考虑以下设置:
如果编译实现文件,它不包含任何代码,因为没有要编译的模板实例。头文件的用户可以实例化
Foo
,但类的主体永远不会在任何 TU 中实例化,因此在拼凑程序时会出现链接器错误。将模板视为代码生成工具而不是实际代码可能会有所帮助,至少出于编译的目的。
Templates are not types. They are only templates. They only become a type when they are instantiated (with a complete set of parameters).
Compilation requires that all necessary type definitions be available at compile time. Moreover, linking requires that all necessary function definitions, including member functions, exist in some translation unit (with inlining providing the usual exemption to the one-definition-rule).
If you put all this together, it follows almost automatically that all template member function definition of a template class must be available for every used template instantiation at some point in the compilation process.
On the other hand, consider the following setup:
If you compile the implementation file, it doesn't contain any code, since there is no template instance to be compiled. A user of the header file may instantiate
Foo<int>
, but the body of the class never gets instantiated in any TU, so you get a linker error when piecing together the program.It may help to think of templates as a code generation tool rather than actual code, at least for the purpose of compilation.
模板是元编程。它们不会(直接)编译成目标代码。只是他们的结果。
Templates are meta programming. They don't get compiled (directly) into object code. Just the results of them.
大多数编译器尚不支持外部模板,这将允许您正在寻找的 cpp/h 类型分开。但是,您仍然可以将模板声明与类似于您想要的实现分开。将声明放在 .h 文件中,将实现放在单独的源文件中,无论您想要什么扩展名(.i 和 .ipp 都很流行),然后
#include
将源文件放在.h 文件。编译器看到单个翻译单元,并且您可以实现代码分离。。
Most compilers do not support external templates yet, which would allow the type of cpp/h separate you are looking for. However, you can still separate template declarations from implementations similar to what you want. Put the declarations in a .h files, put the implementations in a separate source file with whatever extension you want (.i and .ipp are popular), and then
#include
the source file at the bottom of the .h file. The compiler sees a single translation unit, and you get code separation..