Почему встроенные функции C++ в заголовке?

NB это не вопрос о том, как использовать встроенные функции и как они работают, почему они сделали так, как они.

объявление функции-члена класса не должно определять функцию как inline, это только фактическая реализация функции. Например, в заголовочном файле:

struct foo{
    void bar(); // no need to define this as inline
}

Итак, почему встроенная реализация функции классов есть чтобы быть в заголовочном файле? Почему я не могу поместить встроенную функцию ? Если я, где попытаться поместить встроенное определение в .cpp файл я бы получил ошибку по строкам:

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:UsersMeDocumentsVisual Studio 2012ProjectsinlineDebuginline.exe 
: fatal error LNK1120: 1 unresolved externals

8 ответов


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

самый простой способ достичь этого-поместить определение в файл заголовка.

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

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


есть два способа смотреть на это:

  1. встроенные функции объявляются в заголовке, потому что, чтобы встроить вызов функции, компилятор должен иметь возможность видеть тело функции. Для этого наивный компилятор должен иметь тело функции в той же единице перевода, что и вызов. (Современный компилятор может оптимизировать все единицы перевода, поэтому вызов функции может быть встроен, даже если определение функции находится в отдельной единице перевода, но эти оптимизация стоит дорого, не всегда включена и не всегда поддерживается компилятором)

  2. функции, объявленные в заголовке должны быть помечены inline потому что в противном случае каждая единица перевода, включающая заголовок, будет содержать определение функции, а компоновщик будет жаловаться на несколько определений (нарушение правила одного определения). The inline ключевое слово подавляет это, позволяя нескольким единицам перевода содержать (идентичные) определение.

два объяснения действительно сводятся к тому, что inline ключевое слово не совсем то, что вы ожидаете.

компилятор C++ может свободно применять inlining оптимизация (замените вызов функции телом вызываемой функции, сохранив накладные расходы на вызов) в любое время, если это не изменяет наблюдаемое поведение программы.

на inline ключевое слово делает его легче чтобы компилятор применил эту оптимизацию, разрешив определение функции быть видимым в нескольких единицах перевода, но использование ключевого слова не означает компилятор и чтобы встроить функцию, и не использование ключевого слова не запрещает компилятору вставлять функцию.


Это ограничение компилятора C++. Если вы поместите функцию в заголовок, все файлы cpp, в которые она может быть встроена, могут видеть "источник" вашей функции, а вставка может быть выполнена компилятором. В противном случае вставка должна быть выполнена компоновщиком (каждый файл cpp компилируется в файле obj отдельно). Проблема в том, что это было бы намного сложнее сделать в компоновщике. Аналогичная проблема существует с классами/функциями" шаблона". Они должны быть созданы компилятор, потому что компоновщик будет иметь проблемы с их созданием (созданием специализированной версии). Некоторые новые компиляторы / компоновщики могут выполнять компиляцию/компоновку" два прохода", где компилятор выполняет первый проход, затем компоновщик выполняет свою работу и вызывает компилятор для решения неразрешенных проблем (inline/templates...)


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

помните, что C и C++ используют очень упрощенную модель компиляции, где компилятор всегда видит только одну единицу перевода за раз. (Это не удается для экспорта, что является основной причиной только один поставщик фактически реализовал его.)


c++ inline ключевое слово вводит в заблуждение, это не означает "встроенная функция". Если функция определена как встроенная, это просто означает, что она может быть определена несколько раз, пока все определения равны. Это совершенно законно для функции с пометкой inline быть реальной функцией, которая вызывается вместо того, чтобы получать код, встроенный в точку, где он вызывается.

определение функции в файле заголовка необходимо для шаблонов, так как, например, шаблонный класс на самом деле не является класс, это шаблон для класса, который вы можете сделать несколько вариаций. Для того, чтобы компилятор мог, например, сделать Foo<int>::bar() функции при использовании шаблона Foo для создания класса Foo, определением Foo<T>::bar() должен быть виден.


Я знаю, что это старая нить, но подумал, что я должен упомянуть, что extern ключевое слово. Недавно я столкнулся с этой проблемой и решил следующим образом

помощником.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

помощником.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}

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

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.

Встроенные Функции

В C++ Micro - это не что иное, как встроенная функция. Так что теперь micros находятся под контролем компилятора.

  • важно : если мы определим функцию внутри класса, он станет Inline автоматически

код встроенной функции заменяется в месте ее вызова, поэтому он уменьшает накладные расходы на вызов функции.

в некоторых случаях Inlining функции не может работать, Такие как

  • Если статическая переменная используется внутри inline функцию.

  • Если функция сложная.

  • Если рекурсивный вызов функции

  • Если адрес функции, взятого implicitely или явно

функция, определенная вне класса, как показано ниже, может стать встроенной

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

функция, определенная внутри класса, также становится встроенной

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

здесь обе функции getSpeed и setSpeed станут встроенными