为什么必须在同一个翻译单元中声明模板类函数?

时间:2011-11-05 23:12:00

标签: c++ templates compilation 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);
};


#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<int>::Bar(int)'(在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个翻译单元#including foo.h,我们对void Foo::Bar(Type)进行了更改。由于Bar位于头文件中,因此在获得任何结果之前,我们必须等待所有50个翻译单元进行编译。如果我们Bar分别在foo.cpp工作,我们只需要等待1个翻译单元进行编译。有没有办法克服这个问题?

感谢您的任何建议!

4 个答案:

答案 0 :(得分:4)

模板位于编译和链接时间之间。在看到模板声明之后,一个热切的实现可以做很多事情,但是在用模板参数实例化模板之前,不能生成实际的代码。

可以在cpp文件中具有模板类函数,并且可以使用将要使用它的那些参数显式地实例化它。但是,您只能在整个程序中使用这些实例。例如,您可以添加到 foo.cpp

template class Foo<int>;

现在,即使实现位于自己的翻译单元中,您也可以在任何地方使用Foo<int>。但是,您不能使用任何其他类型的Foo<>,因为链接器将无法找到其功能(它们实际上不存在)。

答案 1 :(得分:3)

模板不是类型。它们只是模板。它们只有在实例化(带有一整套参数)时才会成为一种类型。

编译要求在编译时提供所有必需的类型定义。此外,链接要求所有必要的函数定义(包括成员函数)存在于某些翻译单元中(内联提供对单定义规则的通常豁免)。

如果将所有这些放在一起,几乎会自动遵循模板类的所有模板成员函数定义必须可用于编译过程中每个使用过的模板实例

另一方面,请考虑以下设置:

// Header file:
template <typename T> struct Foo { void f(); }

// "Implementation" file
template <typename T> void Foo::f() { /* stuff */ }

如果编译实现文件,则不包含任何代码,因为没有要编译的模板实例。头文件的用户可以实例化Foo<int>,但是类的主体永远不会在任何TU中实例化,因此在拼凑程序时会出现链接器错误。

将模板视为代码生成工具而不是实际代码可能会有所帮助,至少为了编译目的。

答案 2 :(得分:0)

模板为meta programming。它们不会(直接)编译成目标代码。只是他们的结果。

答案 3 :(得分:0)

大多数编译器还不支持外部模板,这将允许您正在寻找的cpp / h类型。但是,您仍然可以将模板声明与类似于您想要的实现分开。将声明放在.h文件中,将实现放在一个单独的源文件中,使用你想要的任何扩展名(.i和.ipp很受欢迎),然后在{。1}} .h文件底部的源文件。编译器会看到一个单独的翻译单元,并且您可以获得代码分离。

#include

/*
* 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);
};

#include "foo.ipp"

#endif /* FOO_H_ */