significato inline nelle interfacce dei moduli


24

Considera il file di intestazione:

class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept : ID(ID_) {}

  int GetID() const noexcept { return ID; }
};

o, in alternativa:

class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept;

  int GetID() const noexcept;
};

inline T::T(int const ID_) noexcept : ID(ID_) {}

inline int T::GetID() const noexcept { return ID; }

In un mondo di pre-moduli, queste intestazioni potrebbero essere incluse testualmente in più TU senza violazioni ODR. Inoltre, poiché le funzioni membro coinvolte sono relativamente piccole, il compilatore probabilmente "inline" (evita le chiamate di funzione quando si utilizza) quelle funzioni, o addirittura ottimizza alcune istanze del Ttutto.

In un recente rapporto sull'incontro in cui C ++ 20 era finito, ho potuto leggere la seguente dichiarazione:

Abbiamo chiarito il significato delle inlineinterfacce nei moduli: l'intento è che corpi di funzioni che non sono esplicitamente dichiarati inlinenon facciano parte dell'ABI di un modulo, anche se quei corpi di funzioni compaiono nell'interfaccia del modulo. Al fine di dare agli autori dei moduli un maggiore controllo sulla loro ABI, le funzioni membro definite nei corpi di classe nelle interfacce dei moduli non sono più implicitamente inline.

Non sono sicuro di non sbagliarmi. Ciò significa che, in un mondo di moduli, affinché il compilatore sia in grado di ottimizzare le chiamate di funzione via, dobbiamo annotarle come inlinese fossero definite in classe?

In tal caso, la seguente interfaccia del modulo sarebbe equivalente alle intestazioni sopra?

export module M;

export
class T
{
private:
  int const ID;

public:
  inline explicit T(int const ID_) noexcept : ID(ID_) {}

  inline int GetID() const noexcept { return ID; }
};

Anche se non ho ancora un compilatore con il supporto dei moduli, vorrei iniziare a usarlo in questo inlinemodo, quando appropriato, per ridurre al minimo il refactoring futuro.

Risposte:


11

Ciò significa che, in un mondo di moduli, affinché il compilatore sia in grado di ottimizzare le chiamate di funzione via, dobbiamo annotarle come inlinese fossero definite in classe?

Di qualche grado.

L'inclinazione è un'ottimizzazione "come se" e l'allineamento può avvenire anche tra le unità di traduzione se il compilatore è abbastanza intelligente.

Detto questo, l'integrazione è più semplice quando si lavora all'interno di una singola unità di traduzione. Pertanto, per promuovere un facile allineamento, una inlinefunzione dichiarata deve avere la sua definizione fornita in qualsiasi unità di traduzione in cui viene utilizzata. Ciò non significa che il compilatore lo incorporerà sicuramente (o certamente non inlineincorporerà alcuna funzione non qualificata), ma rende le cose molto più facili nel processo di allineamento, poiché l'inline sta avvenendo all'interno di una TU piuttosto che tra di loro.

Le definizioni dei membri della classe definite all'interno di una classe, in un mondo pre-modulo, sono dichiarate inlineimplicitamente. Perché? Perché la definizione è all'interno della classe. In un mondo pre-modulo, le definizioni di classe condivise tra TU sono condivise mediante inclusione testuale. I membri definiti in una classe verrebbero pertanto definiti nell'intestazione condivisa tra tali TU. Quindi, se più TU usano la stessa classe, quelle multiple lo stanno facendo includendo la definizione della classe e la definizione dei suoi membri dichiarate nell'intestazione.

Cioè, stai includendo comunque la definizione , quindi perché non farlo inline?

Ovviamente, ciò significa che la definizione di una funzione fa ora parte del testo della classe. Se si modifica la definizione di un membro dichiarato in un'intestazione, questo impone la ricompilazione di ogni file che include quell'intestazione, in modo ricorsivo. Anche se l'interfaccia della classe stessa non cambia, è comunque necessario eseguire una ricompilazione. Quindi rendere implicitamente tali funzioni inlinenon cambia questo, quindi puoi anche farlo.

Per evitarlo in un mondo pre-modulo, puoi semplicemente definire il membro nel file C ++, che non verrà incluso in altri file. Si perde facile allineamento, ma si guadagna tempo di compilazione.

Ma ecco il punto: questo è un artefatto dell'utilizzo dell'inclusione testuale come mezzo per consegnare una classe in più luoghi.

In un mondo modulare, probabilmente vorrai definire ogni funzione membro all'interno della classe stessa, come vediamo in altri linguaggi come Java, C #, Python e simili. Ciò mantiene ragionevole la localizzazione del codice e impedisce di dover digitare nuovamente la stessa firma della funzione, soddisfacendo così le esigenze di DRY.

Ma se tutti i membri sono definiti all'interno della definizione di classe, quindi secondo le vecchie regole, tutti questi membri lo sarebbero inline. E affinché un modulo consenta di essere una funzione inline, l'artefatto del modulo binario dovrebbe includere la definizione di tali funzioni. Ciò significa che ogni volta che si modifica anche una sola riga di codice in una tale definizione di funzione, il modulo dovrà essere costruito, insieme a ogni modulo dipendente da esso, ricorsivamente.

La rimozione dei inlinemoduli impliciti offre agli utenti gli stessi poteri che avevano nei giorni di inclusione testuale, senza dover spostare la definizione fuori dalla classe. Puoi scegliere quali definizioni di funzioni fanno parte del modulo e quali no.


8

Questo viene dal P1779 , appena adottato a Praga pochi giorni fa. Dalla proposta:

Questo documento propone di rimuovere lo stato inline implicito dalle funzioni definite in una definizione di classe allegata a un modulo (denominato). Ciò consente alle classi di trarre vantaggio dall'evitare dichiarazioni ridondanti, mantenendo la flessibilità offerta agli autori dei moduli nella dichiarazione di funzioni con o senza in linea. Inoltre, consente agli amici iniettati di modelli di classe (che non possono essere genericamente definiti al di fuori della definizione di classe) di non essere affatto in linea. Risolve anche il commento NB US90 .

L'articolo (tra le altre cose) ha rimosso la frase:

Una funzione definita all'interno di una definizione di classe è una funzione incorporata.

e ha aggiunto la frase:

Nel modulo globale, una funzione definita all'interno di una definizione di classe è implicitamente incorporata ([class.mfct], [class.friend]).


Il tuo esempio con export module Msarebbe l'equivalente modulare del programma iniziale. Si noti che i compilatori svolgono già funzioni incorporate che non sono annotate inline, è solo che usano anche la presenza della inlineparola chiave nella loro euristica.


Quindi, una funzione in un modulo senza la inlineparola chiave non sarà mai sottolineata dal compilatore, giusto?
metalfox,

1
@metalfox No, non credo sia corretto.
Barry,

1
Vedo. Grazie. È come se fosse definito in un file cpp, il che non significa necessariamente che non sarà incorporato al momento del collegamento.
metalfox
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.