Значение файла .inl в C ++

108

В чем преимущества объявления в файле .inl? Когда мне нужно будет использовать то же самое?

Зузу
источник
3
FWIW, я ненавижу файлы .inl. Зачем разбивать код больше, чем необходимо?
Shog9,
10
@ shog9: Чтобы отделить интерфейс от реализации. Я всегда ненавидел C # и файлы Java, потому что интерфейс очень трудно читать из-за всех деталей реализации messey.
Мартин Йорк
8
@Martin - к сожалению, C ++ дает нам плохую комбинацию обоих миров - интерфейса и части реализации в заголовке, остальной реализации в файле .cpp. Даже если вы избегаете встроенных функций (или помещаете их в файлы .inl), вам придется загромождать интерфейс надоедливыми деталями частных членов, если только вы не можете религиозно использовать идиому pimpl.
Майкл Берр,
5
Да, я никогда не понимал, что заголовки отделяют интерфейс от реализации. Очевидно, они этого не делают. Интерфейс не должен содержать всех закрытых членов.
jalf
@LokiAstari: Честно говоря, Java / C # имеет очень хороший инструментарий, автоматически обеспечивающий структуру интерфейса. Можно было бы сказать иначе: в C ++ вы должны вручную решать проблему, которую полностью могут решить компьютеры.
bluenote10

Ответы:

140

.inlфайлы никогда не являются обязательными и не имеют особого значения для компилятора. Это просто способ структурирования вашего кода, который дает подсказку людям, которые могут его прочитать.

Я использую .inlфайлы в двух случаях:

  • Для определений встроенных функций.
  • Для определений шаблонов функций.

В обоих случаях я помещал объявления функций в заголовочном файле, который включен другими файлами, то я #includeна .inlфайл в нижней части заголовка файла.

Мне это нравится, потому что он отделяет интерфейс от реализации и упрощает чтение файла заголовка. Если вас интересуют детали реализации, вы можете открыть .inlфайл и прочитать его. Если нет, то не обязательно.

Ник Мейер
источник
2
Действительно, в основном речь идет об отделении интерфейса от реализации.
Павел Минаев
1
Я также видел .ipp и .ixx, используемые для встроенных определений, и .tpp и .txx для первого шаблона.
AProgrammer
1
Например, Стандартная библиотека C ++ GNU использует .tccфайлы реализации шаблонов.
musiphil 03
2
@NickMeyer glmиспользует .hpp и .inl точно так же, как вы упомянули выше. Полезно знать, спасибо за отличный ответ :)
legends2k
Так это похоже на заголовок?
Аарон Франке,
90

Ник Мейер прав: компилятор не заботится о расширении файла, который вы включаете, поэтому такие вещи, как «.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 03
1
Если бы функция не была встроенной, вы бы использовали стандартный файл .cpp для реализации?
Bublafus
1
@Bublafus If the function were not inline, you would you standard .cpp file for the implementation part?:: Возможно. Шаблоны - это примеры кода, который обычно нельзя скрыть в файлах .CPP, поэтому в этом случае файл .INL будет обязательным.
paercebal
32

Поскольку никто об этом не упомянул:

Использование файлов .inl для хранения встроенных функций может быть полезно для ускорения компиляции.

Если вы включаете только объявления (.h) там, где вам нужны объявления, и включаете только встроенные реализации (.inl) там, где они вам нужны (т.е., вероятно, только в .cpp и других файлах .inl, а не в .h), он может иметь благотворно влияет на зависимости ваших заголовков.

Это может быть значительной победой для более крупных проектов с большим количеством взаимодействующих классов.

Энди Дж. Бьюкенен
источник
7
+1: мир определенно становится другим, когда вы управляете миллионами строк кода и тысячами файлов.
gatorfax
Значит, вы никогда не должны включать .inl в файлы заголовков? У меня всегда было ощущение, что .inl следует помещать в конец заголовочных файлов, поскольку встроенные функции требуют, чтобы объявление и реализация были доступны сразу.
Icebone1000
1
Icebone1000 не все модули, которые включают заголовок, обязательно хотят использовать встроенные функции, поэтому им не нужно читать реализации, они не должны присутствовать, если они не используются.
Andy J Buchanan
1
Я не понимаю, как это может быть быстрее, поскольку компилятору нужно проделать больше работы, чтобы включить и объединить единицы перевода.
Никос
1
@Nikos Я думаю, он имел в виду быстрее по сравнению с помещением всех ваших встроенных функций в ваши файлы заголовков.
CoffeeTableEspresso
3

По моему опыту, файлы .inl используются для определения встроенных функций. Когда они находятся в файле .inl, файл может быть включен в заголовок для получения встроенных функций и в файл .c для получения обычных определений функций.

Таким образом, один и тот же источник может легче работать с компиляторами, которые не имеют встроенной поддержки функций, а также с компиляторами, которые имеют.

Обычно они используются с прямым кодом C, не часто с кодом C ++, поскольку все компиляторы C ++ поддерживают встроенные функции.

Майкл Берр
источник
Я не вижу смысла делать это только для того, чтобы получить поддержку C. Для C вы просто условно #define inline staticопределите свои встроенные функции в заголовке.
Павел Минаев
Я думаю, это позволяет избежать попадания нескольких копий одной и той же функции в двоичный файл. Я просто говорю, что видел файлы .inl, используемые таким образом, но не то, чтобы это единственный метод (или даже лучший).
Майкл Берр,
1

Я считаю, что это просто соглашение об именах для файла «заголовка», включающего встроенный код. это так, что файлы .h могут содержать определения, а файлы .inl содержат встроенный код, необходимый для шаблонов.

Я не верю, что это что-то большее, чем соглашение об именах, чтобы прояснить цель файла

jcoder
источник