web-dev-qa-db-fra.com

Pourquoi les fonctions en ligne C++ sont-elles dans l'en-tête?

NB Ce n’est pas une question de savoir comment utiliser les fonctions inline ou comment elles fonctionnent, plus pourquoi elles sont faites comme elles sont.

La déclaration d'une fonction membre de classe n'a pas besoin de définir une fonction en tant que inline, il ne s'agit que de l'implémentation réelle de la fonction. Par exemple, dans le fichier d'en-tête:

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

Alors pourquoi l’implémentation en ligne d’une classe fonctionne-t-elle doit-elle se trouver dans le fichier d’en-tête? Pourquoi ne puis-je pas mettre la fonction inline dans le fichier .cpp? Si j'essayais de mettre la définition en ligne dans le fichier .cpp, j'obtiendrais une erreur du type:

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals
96
thecoshman

La définition d'une fonction inline ne doit pas obligatoirement figurer dans un fichier d'en-tête, mais, en raison de la une règle de définition pour les fonctions inline, une définition identique de la fonction doit exister dans chaque unité de traduction qui l'utilise.

Le moyen le plus simple d'y parvenir est de placer la définition dans un fichier d'en-tête.

Si vous souhaitez placer la définition d'une fonction dans un fichier source unique, vous ne devez pas la déclarer inline. Une fonction non déclarée inline ne signifie pas que le compilateur ne peut pas insérer la fonction en ligne.

Que vous deviez ou non déclarer une fonction inline est généralement un choix à faire, en fonction de la version des règles de définition one il est plus logique que vous suiviez; l'ajout de inline et le fait d'être limité par les contraintes suivantes n'a pas beaucoup de sens.

98
CB Bailey

Il y a deux façons de voir les choses:

  1. Les fonctions inline sont déclarées dans l'en-tête car, pour pouvoir intégrer un appel de fonction, le compilateur doit pouvoir visualiser le corps de la fonction. Pour qu'un compilateur naïf puisse faire cela, le corps de la fonction doit être dans la même unité de traduction que l'appel. (Un compilateur moderne peut optimiser plusieurs unités de traduction. Ainsi, un appel de fonction peut être intégré même si la définition de la fonction est définie dans une unité de traduction distincte. Toutefois, ces optimisations coûtent cher, ne sont pas toujours activées et ne sont pas toujours prises en charge par compilateur)

  2. les fonctions déclarées dans l'en-tête doivent être marquées inline car sinon, chaque unité de traduction qui inclut l'en-tête contiendra une définition de la fonction, et l'éditeur de liens se plaindra de plusieurs définitions (violation de la règle de définition unique). Le mot clé inline le supprime, ce qui permet à plusieurs unités de traduction de contenir des définitions (identiques).

Les deux explications se résument vraiment au fait que le mot clé inline ne fait pas exactement ce que vous attendez.

Un compilateur C++ est libre d'appliquer l'inline optimisation (remplacer un appel de fonction par le corps de la fonction appelée, en enregistrant le temps système d'appel) à tout moment, à condition qu'il ne modifie pas le comportement observable du programme.

Le mot clé inline permet au compilateur d'appliquer cette optimisation en {facile, en permettant à la définition de la fonction d'être visible dans plusieurs unités de traduction, mais l'utilisation du mot clé ne signifie pas que le compilateur a insérer la fonction et non utiliser le mot clé n'empêche pas le compilateur de mettre la fonction en ligne.

94
jalf

C'est une limite du compilateur C++. Si vous mettez la fonction dans l'en-tête, tous les fichiers cpp où elle peut être insérée peuvent voir la "source" de votre fonction et l'inlining peut être effectué par le compilateur. Sinon, l'inclusion devrait être effectuée par l'éditeur de liens (chaque fichier cpp est compilé séparément dans un fichier obj). Le problème est qu'il serait beaucoup plus difficile de le faire dans l'éditeur de liens. Un problème similaire existe avec les classes/fonctions "modèle". Ils doivent être instanciés par le compilateur, car l'éditeur de liens aurait des problèmes pour les instancier (en créer une version spécialisée). Certains compilateurs/lieurs plus récents peuvent effectuer une compilation/liaison "en deux passes" lorsque le compilateur effectue une première passe, puis l'éditeur de liens effectue son travail et appelle le compilateur pour résoudre les problèmes non résolus (inline/templates ...).

20
xanatos

La raison en est que le compilateur doit réellement voir ledefinitionafin de pouvoir le déposer à la place de l'appel. 

N'oubliez pas que C et C++ utilisent un modèle de compilation très simpliste, dans lequel le compilateur ne voit toujours qu'une unité de traduction à la fois. (Cela échoue pour l'exportation, qui est la raison principale pour laquelle un seul fournisseur l'a réellement implémentée.)

8
sbi

Le mot clé c ++ inline est trompeur, cela ne signifie pas "inline this function". Si une fonction est définie en ligne, cela signifie simplement qu'elle peut être définie plusieurs fois, à condition que toutes les définitions soient égales. Il est parfaitement légal pour une fonction marquée inline d'être une vraie fonction appelée au lieu d'obtenir du code en ligne au point où elle est appelée.

La définition d’une fonction dans un fichier d’en-tête est nécessaire pour les modèles, par exemple une classe basée sur un modèle n'est pas vraiment une classe, c'est un template pour une classe pour laquelle vous pouvez faire plusieurs variations. Pour que le compilateur puisse, par exemple, créer une fonction Foo<int>::bar() lorsque vous utilisez le modèle Foo pour créer une classe Foo , la définition réelle de Foo<T>::bar() doit être visible.

8
Erik

Je sais que c’est un vieux fil, mais j’ai pensé que je devrais mentionner le mot clé extern. J'ai récemment rencontré ce problème et résolu comme suit

Helper.h

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

Helper.cpp

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

Parce que le compilateur a besoin de les voir pour les inline. Et les fichiers d'en-tête sont les "composants" qui sont généralement inclus dans d'autres unités de traduction.

#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.
1
Leandro T. C. Melo

Fonctions en ligne

En C++, une macro n'est rien d'autre qu'une fonction inline. SO maintenant les macros sont sous le contrôle du compilateur.

  • Important : Si nous définissons une fonction dans la classe, celle-ci deviendra Inline automatiquement

La fonction Code of Inline est remplacée à l'endroit où elle est appelée, ce qui réduit les frais généraux liés à l'appel de la fonction.

Dans certains cas, l’inclusion de fonctions ne peut pas fonctionner, comme 

  • Si la variable statique est utilisée dans la fonction inline.

  • Si la fonction est compliquée.

  • Si appel de fonction récursif 

  • Si l'adresse de la fonction est prise implicitement ou explicitement

La fonction définie en dehors de la classe comme ci-dessous peut devenir inline 

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

La fonction définie dans la classe devient également 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();
} 

Ici, les fonctions getSpeed ​​et setSpeed ​​deviendront en ligne

0
Saurabh Raoot