web-dev-qa-db-fra.com

Y a-t-il encore une utilisation pour inline?

Je pensais que inline était obsolète parce que je lis ici :

Peu importe la façon dont vous désignez une fonction comme inline, c'est une demande que le compilateur est autorisé à ignorer: le compilateur peut développer en ligne certains, tous ou aucun des endroits où vous appelez une fonction désignée comme inline.

Cependant, Angew semble comprendre quelque chose que je ne comprends pas. Dans cette question lui et moi allons et venons un peu, pour savoir si inline est toujours utile.

Cette question est pas une question sur:

Gardant à l'esprit que le compilateur peut inline à volonté, donc inline n'est pas utile là: Où peut-on utiliser inline pour forcer, pas suggéré, un changement de code compilé?

82
Jonathan Mee

Je vais essayer d'expliquer ma "compréhension secrète" de la meilleure façon possible.

Il y a ici deux concepts entièrement distincts. L'un est la capacité du compilateur à remplacer un appel de fonction en répétant le corps de la fonction directement sur le site de l'appel. L'autre est la possibilité de définir une fonction dans plusieurs unités de traduction (= plus d'une .cpp fichier).

Le premier est appelé fonction inline. Le second est le but du mot clé inline. Historiquement, le mot clé inline était également une forte suggestion pour le compilateur qu'il devrait incorporer la fonction marquée inline. Au fur et à mesure que les compilateurs amélioraient l'optimisation, cette fonctionnalité a diminué et l'utilisation de inline comme suggestion pour incorporer une fonction est en effet obsolète. Le compilateur l'ignorera volontiers et insérera autre chose complètement s'il trouve que c'est une meilleure optimisation.

J'espère que nous avons traité la relation explicite inline– inline. Il n'y en a pas dans le code actuel.

Quel est donc le but réel du mot clé inline? C'est simple: une fonction marquée inline peut être définie dans plusieurs unités de traduction sans violer la règle de définition unique (ODR). Imaginez ces deux fichiers:

fichier1.cpp

int f() { return 42; }

int main()
{ return f(); }

fichier2.cpp

int f() { return 42; }

Cette commande:

> gcc file1.cpp file2.cpp

Produira une erreur de l'éditeur de liens, se plaignant que le symbole f est défini deux fois.

Cependant, si vous marquez une fonction avec le mot clé inline, cela indique spécifiquement au compilateur et à l'éditeur de liens: "Vous vous assurez que plusieurs définitions identiques de cette fonction ne pas entraîner des erreurs! "

Donc, ce qui suit fonctionnera:

fichier1.cpp

inline int f() { return 42; }

int main()
{ return f(); }

fichier2.cpp

inline int f() { return 42; }

La compilation et la liaison de ces deux fichiers ne produiront aucune erreur de l'éditeur de liens.

Notez bien sûr que la définition de f ne doit pas nécessairement figurer dans les fichiers textuellement. Il peut provenir d'un #included fichier d'en-tête à la place:

f.hpp

inline int f() { return 42; }

fichier1.cpp

#include "f.hpp"

int main()
{ return f(); }

fichier2.cpp

#include "f.hpp"

Fondamentalement, pour pouvoir écrire une définition de fonction dans un fichier d'en-tête, vous devez la marquer comme inline, sinon cela entraînera plusieurs erreurs de définition.


La dernière pièce du puzzle est la suivante: pourquoi le mot-clé est-il réellement orthographié inline alors qu'il n'a rien à voir avec l'incrustation? La raison est simple: pour incorporer une fonction (c'est-à-dire pour remplacer un appel à celle-ci en répétant son corps sur le site de l'appel), le compilateur doit avoir le corps de la fonction en premier lieu.

C++ suit un modèle de compilation distinct, dans lequel le compilateur n'a pas accès aux fichiers objets autres que celui qu'il produit actuellement. Par conséquent, pour pouvoir incorporer une fonction, sa définition doit faire partie de l'unité de traduction actuelle. Si vous voulez pouvoir l'inclure dans plusieurs unités de traduction, sa définition doit être dans chacune d'elles. Normalement, cela entraînerait une erreur de définition multiple. Donc, si vous mettez votre fonction dans un en-tête et #include sa définition partout pour activer son inlining partout, vous devez le marquer comme inline pour éviter plusieurs erreurs de définition.

Notez que même aujourd'hui, bien qu'un compilateur insère n'importe quelle fonction comme bon lui semble, il doit toujours avoir accès à la définition de cette fonction. Ainsi, bien que le mot clé inline ne soit pas requis comme indice "veuillez l'intégrer", vous pouvez toujours trouver que vous devez l'utiliser pour activer le compilateur pour effectuer l'inline s'il le souhaite. Sans cela, vous ne pourrez peut-être pas obtenir la définition dans l'unité de traduction, et sans la définition, le compilateur ne peut tout simplement pas incorporer la fonction.

Le compilateur ne peut pas. L'éditeur de liens peut. Les techniques d'optimisation modernes incluent la génération de code Link-Time (a.k.a. Whole Program Optimization), où l'optimiseur est exécuté sur tous les fichiers objet dans le cadre du processus de liaison, avant la liaison réelle. Dans cette étape, toutes les définitions de fonction sont bien sûr disponibles et l'inlining est parfaitement possible sans qu'un seul mot clé inline ne soit utilisé n'importe où dans le programme. Mais cette optimisation est généralement coûteuse en temps de construction, en particulier pour les grands projets. Dans cet esprit, s'appuyer uniquement sur LTCG pour l'inline peut ne pas être la meilleure option.


Pour être complet: j'ai un peu triché dans la première partie. La propriété ODR n'est en fait pas une propriété du mot clé inline, mais des fonctions en ligne (qui est un terme du langage). Les règles pour les fonctions en ligne sont les suivantes:

  • Peut être défini dans plusieurs unités de traduction sans provoquer d'erreurs de l'éditeur de liens
  • Doit être défini dans chaque unité de traduction dans laquelle il est utilisé
  • Toutes ses définitions doivent être identiques jeton pour jeton et entité pour entité

Le mot clé inline transforme une fonction en fonction inline. Une autre façon de marquer une fonction comme étant en ligne est de la définir (pas seulement de la déclarer) directement dans une définition de classe. Une telle fonction est automatiquement intégrée, même sans le mot clé inline.

75
Angew

inline n'est plus qu'un spécificateur de liaison externe maintenant, pour les raisons que vous avez indiquées.

Alors oui, il a une utilité, mais c'est différent des fonctions réellement intégrées. Il vous permet de définir plusieurs fois la même méthode entre les unités de compilation et de les lier correctement ensemble, au lieu d'obtenir plusieurs erreurs de définition.

//header.h
inline void foo() {}
void goo() {}

//cpp1.cpp
#include "header.h"

//cpp2.cpp
#include "header.h"

// foo is okay, goo breaks the one definition rule (ODR)

En fait, le forçage de la fonction inline dépend du compilateur, certains peuvent être pris en charge via des attributes ou pragmas spécifiques ou (__forceinline) ou autre chose.

Autrement dit, il vous permet de définir des fonctions dans les en-têtes sans casser l'ODR ...

45
Luchian Grigore