Vantaggi o meno dell'utilizzo di const durante l'assegnazione di variabili [duplicate]

3

Se ho un codice che assegna un valore a una variabile e tale valore non cambia, è considerato buona pratica utilizzare const durante l'assegnazione ?

per es.

void foo()
{
   int n = some_function_which_computes_n(); // <-- this could (should?) be const

   // imagine some code here which uses n, but doesn't change it
}

Vorrei usare const int n qui, ma la maggior parte dei miei co-sviluppatori pensa che sia un po 'una perdita di tempo e di battitura. Non vedono alcuna ragione per fare il miglio supplementare.

C'è qualche ragione convincente per farlo o è più una questione di gusti personali? Aiuta il compilatore in qualsiasi modo a produrre codice migliore o è una codifica difensiva borderline? So che tecnicamente probabilmente è più corretto usare const qui, ma non è molto interessante.

    
posta WalderFrey 29.08.2017 - 18:43
fonte

4 risposte

9

Ci sono diversi vantaggi:

  • chiarisce la tua intenzione: tutti capiranno che questa variabile non dovrebbe variare.
  • evita errori stupidi, in cui un errore di battitura avrebbe causato la modifica del valore
  • forza la disciplina: se usi puntatori o riferimenti a quella variabile, il compilatore si lamenterà se si tratta di un puntatore non const o di un riferimento, che potrebbe portare a sottili errori.
  • inoltre, ciò incoraggerà la disciplina nel valutare la costanza degli argomenti passati per riferimento o tramite puntatori, che alla fine eviteranno errori e codici più sottili che è più facile da verificare.
  • consente al compilatore di formulare assunzioni e ottimizzazioni, che altrimenti non potrebbero essere fatte in determinate circostanze.

Lascia che sviluppi l'ultimo punto con un esempio piccolo e sciocco.

L'approccio non const :

extern void f(int *p);  // function f() takes a non const pointer

int main()
{
     int k = 10; 
     f(&k);             // the compiler has to assume that f() could change k
     int i = k*k+2*k+1; // the compiler has to make the operation
     std::cout<<i; 
}

La parte rilevante di l'assemblaggio ha il seguente aspetto:

    mov     DWORD PTR [rsp+12], 10         // store value in k
    call    f(int*)
    mov     eax, DWORD PTR [rsp+12]        // load the potentially changed k
    lea     esi, [rax+2]                   // and make the computation
    imul    esi, eax
    add     esi, 1                         // here we are ready to display value

Qui il compilatore deve presupporre che k possa essere modificato da f() , perché questa funzione è sconosciuta. Quindi il codice generato deve fare il calcolo di i , anche se sapessimo che f() non cambia il valore puntato a.

L'approccio const:

int main()
{
     const int k = 10; 
     f(const_cast<int*>(&k));  // ATTENTION: we have to be sure about the cast!!  
     int i = k*k+2*k+1; 
     std::cout<<i; 
}

Innanzitutto, il const_cast indica che stiamo correndo un rischio. Quindi dobbiamo essere veramente sicuri che f() non cambierà il valore puntato a (se non sarebbe un comportamento indefinito).

Ma il compilatore potrebbe quindi generare un codice molto più veloce:

    mov     DWORD PTR [rsp+12], 10
    call    f(int*)
    mov     esi, 121      // the compiler could make the calculation upfront!  

L'approccio clean const:

Ovviamente const_cast è qualcosa di rischioso, e dovrebbe essere evitato se possibile. Ed è qui che compare il vantaggio di secondo livello dell'uso di const : diventi sensibile a questo tipo di sottigliezze e inizierai ad aumentare la const disciplina, riscrivendo f() correttamente rendendo esplicita la costanza bene:

extern void f(const int *p);  // if parameter remains const, tell it ! 

int main()
{
     const int k = 10; 
     f(&k);             // the compiler knows that k will not change
     int i = k*k+2*k+1; // the compiler knows it's 121
     std::cout<<i; 
}

Questo codice è un evento migliore, perché non solo sei sicuro che k sia costante, ma tutti ora sanno che f() non cambia il valore puntato e il compilatore può fare una propagazione costante migliore.

Naturalmente, passare i parametri per riferimento produrrebbe risultati simili.

    
risposta data 29.08.2017 - 19:27
fonte
3

L'uso coerente di const è, approssimativamente, Modulo di assegnazione singola statica. (C'è dell'altro oltre a questo, ma questo è l'aspetto dal quale prende il nome.) Questo è quello che molti compilatori, incluso clang, traducono il codice in un passaggio intermedio durante l'ottimizzazione e si converte facilmente in Continuation-Passing Style, che altri compilatori utilizzare.

Di solito non ti interessa fare il lavoro del compilatore più facile, però. Personalmente trovo che mi aiuti a scrivere il codice corretto.

Supponiamo che tu faccia tutte le variabili (con alcune eccezioni di senso comune come gli array per gli algoritmi sul posto e gli indici di loop) const . Se hai bisogno di un altro valore locale intermedio, per esso definisci una nuova variabile const . L'ottimizzatore sarà in grado di vedere quando, per la prima volta e l'ultima volta, utilizzi ciascuna variabile const locale e ne determini la durata nel tempo.

Se non modifichi la variabile, non c'è perdita di efficienza e forse anche qualche ottimizzazione se il compilatore non ha potuto dedurre che la variabile non può essere modificata. In effetti, stai solo facendo il primo passo di ottimizzazione da solo. La variabile, per tutta la sua durata, conterrà solo una cosa e non sarà mai non inizializzata o modificata. È una semplice invariante, una cosa in meno che può andare storta. Se modifichi la variabile, si tratta di un errore logico e il compilatore lo catturerà ora. Senza const , avresti un bug quando la variabile conteneva qualcosa di diverso da quello che pensavi, e solo controllando il valore nel debugger te lo direbbe.

    
risposta data 30.08.2017 - 04:35
fonte
1

Inizierò col dire che per lo più sono d'accordo sul fatto che usarlo sia buono. Detto questo, mi imbatto in 1 svantaggio di volta in volta. Lavoro su un codebase in cui uno sviluppatore ha utilizzato const molto. Era soprattutto una buona idea e il suo codice è abbastanza buono e facile da usare. Scriverà cose del genere:

const int someVal = 1;
const int someOtherVal = 2;

for (int i = 0; i < maxVal; i++)
{
    ...some code...
    callFunction(someVal, someOtherVal);
    ...more code...
}

Niente fuori dall'ordinario. Ma ora devo modificare il codice in modo tale che il valore di someVal sia basato su un condizionale. Normalmente, lo cambierei in:

if (condition)
{
    someVal = 1;
}
else
{
    someVal = 2;
}

Ma poiché è stato dichiarato const , non posso apportare questa modifica. Potrei usare un ternario se la condizione è ragionevole:

const int someVal = condition ? 1 : 2;

Ma spesso condition è un'espressione lunga o implica il calcolo di qualcosa. Quindi le opzioni non sono const , o chiama una funzione per calcolare il valore:

const int someVal = figureOutSomeVal(<some set of values>);

Ora sto scrivendo più codice e passando argomenti che posso rovinare, ecc.

Fare questo una volta non è un grosso problema. Ma quando c'è un cambiamento importante e improvvisamente tutte le cose const ora hanno definizioni variabili, può essere un problema. Posso sicuramente vedere alcuni sviluppatori che non amano doverlo affrontare.

Detto questo, penso che nella maggior parte dei casi valga il possibile dolore futuro per usare const .

    
risposta data 30.08.2017 - 06:49
fonte
0

Nessun vantaggio in termini di prestazioni, ma è una buona pratica in particolare nella collaborazione.

Può anche essere utile per prevenire accidentali che potrebbero verificarsi quando qualcosa viene passato per riferimento.

Lo consiglierei se è qualcosa che deve rimanere lo stesso, ma se viene usato molto localmente e alla fine viene eliminato dal cestino, allora è meno importante.

    
risposta data 29.08.2017 - 18:53
fonte

Leggi altre domande sui tag