仅使用头文件编译C ++ .lib?

时间:2009-03-08 14:14:49

标签: c++ code-organization

我正在编译C ++静态库,并且因为所有类都是模板化的,所以类定义和实现都在头文件中。结果,似乎(在visual studio 2005下)我需要创建一个包含所有其他头文件的.cpp文件,以便它能够正确地编译到库中。

为什么会这样?

9 个答案:

答案 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等更高级的功能分发库,但其他模板使用辅助库,模板本身不以库的形式分发。