.inl文件在C ++中的意义


108

在.inl文件中声明的好处是什么?什么时候需要使用?


3
FWIW,我讨厌.inl文件。为什么要对代码进行不必要的拆分?
Shog9年9

10
@ shog9:将接口与实现分开。我一直讨厌C#和Java文件,因为所有杂乱无章的实现细节都很难读取接口。
马丁·约克

8
@Martin-不幸的是,C ++给了我们两个世界的糟糕组合-标头中的接口和部分实现,.cpp文件中的其余实现。即使您避免使用内联函数(或将它们放在.inl文件中),也必须使接口的私有成员的细节变得混乱,除非您能够虔诚地使用pimpl习惯用法。
Michael Burr,2009年

5
是的,我从不理解标头将接口与实现分开的说法。他们显然没有。接口不应包含所有私有成员。
jalf

@LokiAstari:公平地说,Java / C#具有很好的工具,可以自动提供界面轮廓。一个可能的说法是相反的:在C ++中,您必须手动解决一个问题,而这可以由计算机完全解决。
bluenote10 '16

Answers:


139

.inl文件永远不是必需的,并且对编译器没有特殊意义。这只是一种结构化代码的方式,向可能阅读该代码的人员提供了提示。

.inl在两种情况下使用文件:

  • 用于内联函数的定义。
  • 用于功能模板的定义。

在这两种情况下,我都将函数的声明放在其他文件包含的头文件中,然后#include.inl文件放在头文件的底部。

我喜欢它,因为它使接口与实现分开,并使头文件更易于阅读。如果您关心实现细节,则可以打开.inl文件并阅读。如果不这样做,则不必。


2
确实,这主要是关于将接口与实现分开。
帕维尔米纳夫

1
我还看到.ipp和.ixx用于内联定义,而.tpp和.txx用于模板一。
AProgrammer

1
例如,GNU标准C ++库.tcc用于模板实现文件。
musiphil 2013年

1
@NickMeyer glm使用.hpp和.inl的方式与您上面提到的完全相同。众所周知,感谢您的出色回答:)
legends2k

像标题吗?
亚伦弗兰克

90

尼克·迈耶Nick Meyer)是对的:编译器不在乎所包含文件的扩展名,因此,诸如“ .h”,“。hpp”,“。hxx”,“。hh”,“。inl”, “ .inc”等是一个简单的约定,用于明确说明文件应包含的内容。

最好的示例是没有扩展名的STL头文件。

通常,“。inl”文件确实包含内联代码(因此,扩展名为“ .inl”)。

当您在标头代码之间具有依赖性循环时,必须使用那些文件“ .inl” 。

例如:

// A.hpp
struct A
{
    void doSomethingElse()
    {
       // Etc.
    }

    void doSomething(B & b)
    {
       b.doSomethingElse() ;
    }
} ;

和:

// B.hpp
struct B
{
    void doSomethingElse()
    {
       // Etc.
    }

    void doSomething(A & a)
    {
       a.doSomethingElse() ;
    }
} ;

您无法对其进行编译,包括使用前向声明。

然后,解决方案是将定义和实现分解为两种头文件:

  • hpp 用于标题声明/定义
  • inl 用于标头实现

分为以下示例:

// A.hpp

struct B ;

struct A
{
    void doSomethingElse() ;
    void doSomething(B & b) ;
} ;

和:

// A.inl
#include <A.hpp>
#include <B.hpp>

inline void A::doSomethingElse()
{
   // Etc.
}

inline void A::doSomething(B & b)
{
   b.doSomethingElse() ;
}

和:

// B.hpp

struct A ;

struct B
{
    void doSomethingElse() ;
    void doSomething(A & a) ;
} ;

和:

// B.INL
#include <B.hpp>
#include <A.hpp>

inline void B::doSomethingElse()
{
   // Etc.
}

inline void B::doSomething(A & a)
{
   a.doSomethingElse() ;
}

这样,您可以在自己的源代码中包含所需的任何“ .inl”文件,它将起作用。

同样,包含文件的后缀名称并不是很重要,只是它们的用途。


5
这说明了分离的真正好处(或必要性),应该选择答案。
musiphil

如果该函数不是内联函数,那么您会为实现部分使用标准的.cpp文件吗?
Bublafus 2015年

@Bublafus If the function were not inline, you would you standard .cpp file for the implementation part?::可能。模板是通常无法隐藏在.CPP文件中的代码示例,因此在这种情况下,.INL文件将是必需的。
paercebal 2015年

32

由于没有人提到它:

使用.inl文件存储内联函数对于加速编译很有用。

如果仅在需要声明的地方包含声明(.h),仅在需要声明的地方包含内联实现(.inl)(即可能仅在.cpp和其他.inl文件中,而不是.h),则它可以具有对您的标头依赖性的有益影响。

对于具有许多交互类的较大项目而言,这可能是一个重大胜利。


7
+1:管理数百万行代码和数以千计的文件时,世界绝对是一个不同的地方。
gatorfax

所以您永远不要在头文件中包含.inl?我一直觉得.inl应该放在头文件的底部,因为内联函数需要立即声明和实现。
Icebone1000

1
Icebone1000并非所有包含标头的模块都一定要使用内联函数,因此它们不需要读取实现,如果不使用它们,则不需要存在。
Andy J Buchanan 2013年

1
我不知道它怎么会更快,因为编译器必须做更多的工作来包含和合并翻译单元。
Nikos '18

1
@Nikos我认为他的意思是相对于将所有内联函数放入头文件中而言,速度更快。
CoffeeTableEspresso

3

以我的经验,.inl文件用于定义内联函数。当它们位于.inl文件中时,该文件可以包含在标头中以获取内联函数,而可以包含在.c文件中以获取常规函数定义。

这样,同一源可以更轻松地与不具有内联函数支持的编译器以及具有内联函数支持的编译器一起使用。

它们通常与直接的C代码一起使用,而不是与C ++代码一起使用,因为所有C ++编译器都支持内联函数。


我认为这样做并不是为了获得C支持。对于C,您只需有条件#define inline static,并在标头中定义内联函数。
帕维尔米纳夫

我猜这可以避免同一函数的多个副本最终出现在二进制文件中。我只是说我已经看到.inl文件以这种方式使用,而不是这是唯一的技术(甚至是最好的技术)。
Michael Burr,2009年

1

我相信这只是“标头”文件包含内联代码的命名约定。这样,.h文件可以包含定义,.inl文件包含模板必需的内联代码。

我不相信除了命名约定之外,还有什么其他东西可以使文件的目的明确

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.