我正在编译C ++静态库,并且因为所有类都是模板化的,所以类定义和实现都在头文件中。结果,似乎(在visual studio 2005下)我需要创建一个包含所有其他头文件的.cpp文件,以便它能够正确地编译到库中。
为什么会这样?
答案 0 :(得分:8)
编译器不编译头文件,因为这些文件应包含在源文件中。在进行任何编译之前,预处理器会从任何包含的头文件中获取所有代码,并将代码放入包含它们的源文件中。如果编译器也应该编译头文件,那么你可以在很多方面有多个定义。
示例,这是预处理器看到的内容:
[foo.h]
void foo();
-
[mysource.cpp]
#include "foo.h"
int main()
{
foo();
}
这就是编译器所看到的:
[mysource.cpp]
void foo();
int main()
{
foo();
}
答案 1 :(得分:4)
即使您要创建.cpp文件,您仍然不会收到任何内容。您需要实例化模板才能将它们放入库中。
看看这里 http://www.parashift.com/c%2B%2B-faq-lite/templates.html#faq-35.13 关于如何使用具体类型实例化模板。
答案 2 :(得分:2)
在c ++中,模板只是实际类的元定义。编译模板化类时,编译器实际上会为传入的特定数据类型生成实际类的代码(模板只是要复制的“模式”)。
e.g。如果您有以下代码
struct MyTemplate
{
private:
float MyValue;
public:
float Get() { return MyValue; }
void Set(float value) { MyValue = value; }
};
void main()
{
MyTemplate v1;
MyTemplate v2;
v1.Set(5.0f);
v2.Set(2);
v2.Get();
}
编译器实际看到的是
struct CompilerGeneratedNameFor_MyTemplate_float
{
private:
float MyValue;
public:
float Get() { return MyValue; }
void Set(float value) { MyValue = value; }
};
struct CompilerGeneratedNameFor_MyTemplate_int
{
private:
int MyValue;
public:
int Get() { return MyValue; }
void Set(int value) { MyValue = value; }
};
void main()
{
CompilerGeneratedNameFor_MyTemplate_float v1;
CompilerGeneratedNameFor_MyTemplate_int v2;
v1.Set(5.0f);
v2.Set(2);
v2.Get();
}
正如您可能看到的那样,在您实际声明模板的实例之前,编译器实际上并不知道要生成的代码。这意味着模板无法编译到库中,因为它不知道模板实际上最终会是什么。有关这方面的好消息是,如果只分发包含模板定义的头文件,则实际上不需要编译或包含任何库。
另外,作为旁注,'#include'预编译器命令实际上只是告诉预编译器将'#include'替换为该文件中的所有内容。
答案 3 :(得分:2)
如果所有代码都在.h文件中,那么您不需要编译静态库来使用代码。
所有代码都可以在编译时使用库,因此在链接时不需要任何代码。
答案 4 :(得分:1)
你正试图创造一些不必要的东西。大多数C库(以及所有C ++库)分为两部分:
foo.h
)foo.lib
)对于C ++模板代码,您的所有库必须由最终用户编译,因为这是模板的工作方式。没有理由提供预编译的库。在这种情况下,您可以将您的库分布视为:
foo.h
)foo-inl.h
)正如Niel所说,实现仅仅是为了您自己的测试目的是有用的,并且可能值得与库本身分发。所以你应该有一套单独的单元测试来运行你的代码;但这些测试不应该是图书馆本身的一部分。
答案 5 :(得分:1)
如果您的库全部在头文件中实现,则无需构建任何二进制文件即可使用它。那就是说。我通常在头文件库的初始开发阶段创建一个.cpp文件。为什么?在实际使用之前,编译器不会尝试编译甚至解析模板。有一个.cpp文件,并在那里有一些虚拟代码来实例化模板,这有助于我在开发过程中更早地发现语法错误。所以我可以添加一些模板代码,命中编译,修复语法错误,添加更多代码,编译等等。如果您在添加数百行代码后尝试寻找一些愚蠢的语法错误,您就会明白我的意思。一旦我的库准备好进行单元测试,我将删除.cpp文件,并依靠单元测试来推动我的开发。
另外,如果您只使用VC ++编译代码,那么您需要注意的一件事是VC ++在实际使用之前不会尝试编译所有模板成员函数。例如:
template <typename T>
class MyTemplate
{
public:
MyTemplate() {} // default constructor
MyTemplate(int) {
1 = 2
// other syntax error code here
}
};
void f() { MyTemplate<int> myt(); } // compile fine in VC
void g() { MyTemplate<int> myt(1); } // syntax error
f ++将使用VC ++ 2003编译得很好,g ++会捕获语法错误。我认为VC8和VC9也存在同样的问题。
答案 6 :(得分:0)
考虑标准模板库。当您在另一个项目中使用它们时,您的模板化类将被编译。
答案 7 :(得分:0)
其他人对于未编译到库中的模板的说法是正确的。但是,仍然值得强迫它们被编译器看到(通过#including它们在.cpp文件中),因为这样它们至少会被检查语法。
答案 8 :(得分:0)
如果你所有的类都是模板,你不需要生成.lib,看一下它们或者它们没有它们分发的.lib的stlport [1]。
模板在使用时会被编译。
[1]严格地说,他们为正则表达式,iostream等更高级的功能分发库,但其他模板使用辅助库,模板本身不以库的形式分发。