C’è ancora un uso per inline?

Ho creduto, in inline era obsoleto perché ho letto qui :

Indipendentemente dal modo in cui si designa una funzione come inline , è una richiesta che il compilatore può ignorare: il compilatore potrebbe espandere in linea alcuni, tutti o nessuno dei punti in cui si chiama una funzione designata come inline .

Tuttavia, Angew sembra capire qualcosa che io non conosco. In questa domanda io e lui andiamo avanti e indietro un po ‘, se in inline è ancora utile.

Questa domanda non è una domanda su:

  • L’uso storico di inline o inline potrebbe ancora essere usato per suggerire al compilatore funzioni inline : quando dovrei scrivere la parola chiave ‘inline’ per una funzione / metodo? .
  • I vantaggi o gli svantaggi del codice funzione inlining: i vantaggi delle funzioni inline in C ++?
  • Forzare il compilatore al codice funzione inline : forza la funzione inline in un’altra unità di traduzione

Tenendo presente che il compilatore può essere inline a piacere, quindi in inline non è utile lì: dove può essere usato inline per forzare, non suggerire , un cambiamento nel codice compilato?

Cercherò di spiegare la mia “comprensione segreta” nel modo migliore ansible.

Ci sono due concetti completamente separati qui. Uno è la capacità del compilatore di sostituire una chiamata di funzione ripetendo il corpo della funzione direttamente nel sito di chiamata. L’altra è la possibilità di definire una funzione in più di una unità di traduzione (= più di un file .cpp ).

Il primo è chiamato inlining della funzione. Il secondo è lo scopo della parola chiave in inline . Storicamente, la parola chiave inline era anche un forte suggerimento per il compilatore che dovrebbe inline la funzione contrassegnata in inline . Dato che i compilatori sono migliorati nell’ottimizzazione, questa funzionalità si è ritirata e l’uso di inline come suggerimento per incorporare una funzione è effettivamente obsoleto. Il compilatore lo ignorerà felicemente e inline qualcos’altro completamente se trova che è una ottimizzazione migliore.

Spero che abbiamo affrontato l’esplicita relazione inline -inline. Non c’è nessuno nel codice corrente.

Quindi, qual è lo scopo effettivo della parola chiave in inline ? È semplice: una funzione contrassegnata in inline può essere definita in più di un’unità di traduzione senza violare la regola di definizione singola (ODR). Immagina questi due file:

file1.cpp

 int f() { return 42; } int main() { return f(); } 

file2.cpp

 int f() { return 42; } 

Questo comando:

 > gcc file1.cpp file2.cpp 

Produrrà un errore del linker, lamentando che il simbolo f è definito due volte.

Tuttavia, se contrassegni una funzione con la parola chiave inline , specifica specificamente al compilatore e al linker: “Voi ragazzi assicuratevi che più definizioni identiche di questa funzione non comportino errori!”

Quindi il seguente funzionerà:

file1.cpp

 inline int f() { return 42; } int main() { return f(); } 

file2.cpp

 inline int f() { return 42; } 

La compilazione e il collegamento di questi due file non produrranno errori di linker.

Si noti che, naturalmente, la definizione di f non deve essere presente nei file letteralmente. Può invece provenire da un file di intestazione #include d:

f.hpp

 inline int f() { return 42; } 

file1.cpp

 #include "f.hpp" int main() { return f(); } 

file2.cpp

 #include "f.hpp" 

Fondamentalmente, per essere in grado di scrivere una definizione di funzione in un file di intestazione, è necessario contrassegnarlo come inline , altrimenti causerà errori di definizione multipli.


L’ultimo pezzo del puzzle è: perché la parola chiave effettivamente è scritta in inline quando non ha nulla a che fare con l’inlining? La ragione è semplice: per inline una funzione (cioè per sostituire una chiamata ad essa ripetendo il suo corpo sul sito di chiamata), il compilatore deve avere il corpo della funzione in primo luogo.

C ++ segue un modello di compilazione separato, in cui il compilatore non ha accesso ai file object diversi da quello che sta attualmente producendo. Pertanto, per essere in grado di integrare una funzione, la sua definizione deve far parte dell’unità di traduzione corrente. Se vuoi essere in grado di inserirlo in più di una unità di traduzione, la sua definizione deve essere in tutte. Normalmente ciò comporterebbe un errore di definizione multipla. Quindi, se metti la tua funzione in un’intestazione e #include sua definizione ovunque per abilitarne l’allineamento ovunque, devi contrassegnarla come inline per evitare errori di definizione multipli.

Si noti che anche oggi, mentre un compilatore integra qualsiasi funzione, è necessario che abbia ancora accesso alla definizione di quella funzione. Quindi, anche se la parola chiave inline non è richiesta come suggerimento “per favore in linea”, potresti comunque trovare che devi usarla per abilitare il compilatore a fare il inlining se sceglie di farlo. Senza di esso, potresti non essere in grado di ottenere la definizione nell’unità di traduzione, e senza la definizione, il compilatore semplicemente non può inline la funzione.

Il compilatore non può. Il linker può. Le moderne tecniche di ottimizzazione comprendono la generazione di codice in tempo reale (ovvero l’intera ottimizzazione del programma), in cui l’ottimizzatore viene eseguito su tutti i file object come parte del processo di collegamento, prima del collegamento effettivo. In questo passaggio, tutte le definizioni di funzioni sono ovviamente disponibili e l’inlining è perfettamente ansible senza una singola parola chiave inline utilizzata in qualsiasi parte del programma. Ma questa ottimizzazione è generalmente costosa nei tempi di costruzione, soprattutto per i progetti di grandi dimensioni. Con questo in mente, affidarsi esclusivamente a LTCG per l’inlining potrebbe non essere l’opzione migliore.


Per completezza: ho imbrogliato leggermente nella prima parte. La proprietà ODR non è in realtà una proprietà della parola chiave inline , ma delle funzioni inline (che è un termine della lingua). Le regole per le funzioni inline sono:

  • Può essere definito in più unità di traduzione senza causare errori del linker
  • Deve essere definito in ogni unità di traduzione in cui viene utilizzato
  • Tutte le sue definizioni devono essere token-to-token e entity framework-per- quadro identiche

La parola chiave inline trasforma una funzione in una funzione inline. Un altro modo per contrassegnare una funzione come inline è definire (non solo dichiarare) direttamente in una definizione di class. Tale funzione è automaticamente in linea, anche senza la parola chiave inline .

inline è per lo più solo un identificatore di linkage esterno, per le ragioni che hai affermato.

Quindi sì, ha un uso, ma è diverso rispetto alle funzioni di inlining. Consente di definire più volte lo stesso metodo tra le unità di compilazione e di collegarle correttamente, anziché ottenere più errori di definizione.

 //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) 

In realtà la forzatura della funzione di inlining spetta al compilatore, alcuni potrebbero avere il supporto tramite attribute specifici o pragma s o ( __forceinline ) o quant’altro.

In poche parole, ti permette di definire le funzioni nelle intestazioni senza rompere l’ODR …