2010-08-25 5 views
6

Avrebbe senso eseguire l'operazione "constify" in C/C++ che rende una variabile const?Avrebbe senso avere un'operazione di "conscient" in C++?

Ecco un esempio in cui potrebbe essere utile, dove ovviamente non vogliamo di dichiararla const ancora nella prima riga:

std::vector<int> v; 
v.push_back(5); 
constify v; // now it's const 

Attualmente, senza una tale possibilità, si avrebbe dovuto introdurre un'altra variabile per ottenere lo stesso effetto: (? o utilizzare swap)

std::vector<int> v0; 
v0.push_back(5); 
const std::vector<int>& v = v0; 

Questo è più confusa dal momento che aggiunge un nuovo nome nel campo di applicazione ed è necessario fare un riferimento per evitare di copiare l'intero vettore.

+3

Cosa succede se si consola v in un solo ramo di un if-then-else? –

+1

Quindi probabilmente dovrebbe essere 'const' solo in quell'ambito. – Frank

+3

No. Non avrebbe senso. –

risposta

19

Francamente, trovo meno confusione se una variabile è o const o no, che se questo può cambiare.


Elaborare un po 'su questo: La ragione per cui di solito si vuole fare questo è perché non è possibile inizializzare una variabile const il modo in cui si desidera. std::vector è un buon esempio di questo. Beh, per una volta, il prossimo standard introduce una sintassi di inizializzazione universale che rende possibile tutto ciò:

const std::vector<int> cvi = { 1, 2, 3, 4, 5, 42 }; 

Tuttavia, anche senza roba C++ 1x' a portata di mano, e anche con i tipi che non consentire questa sintassi di inizializzazione, è possibile creare sempre una funzione di supporto per fare ciò che si vuole:

const std::vector<int>& cvi = create_my_vector(); 

o, se si vuole essere di fantasia:

const std::vector<int>& cvi = compile_time_list<1,2,3,4,5,42>::create_vector(); 

Annotare la &. Non ha senso copiare il risultato della chiamata di funzione, poiché il binding di un valore di rvalue a un riferimento const estende la sua durata fino alla fine della durata del riferimento.
Naturalmente, la ricompilazione con un compilatore che supporta C++ 1x 'semantica di spostamento renderà tali ottimizzazioni praticamente inutili. Ma legare un rvlaue a un riferimento const potrebbe essere ancora più veloce dello spostamento di un vettore ed è improbabile che sia più lento.
Con C++ 1x, è possibile creare anche funzioni lambda che lo facciano al volo. C++ fornisce solo un enorme arsenale di strumenti. IME, non importa quanto tu abbia pensato, qualcun altro dovrebbe venire con un'altra idea per fare la stessa cosa. E spesso uno migliore del tuo.


Tuttavia, IME questo problema in genere viene fornito con troppo codice in troppo poche funzioni. E poi non si applica solo alla costanza, ma anche a tratti simili - come a cui si riferisce un riferimento.
Un classico è l'uso-uno-di-diversi-possibili-flussi.Invece di questo

int main(int argc, char* argv[]) 
{ 
    std::istream* istrm = NULL; 
    std::ifstream ifs; 
    if(argc > 1) 
    { 
    ifs.open(argv[1]); 
    if(ifs.good()) 
     istrm = &ifs; 
    } 
    if(!istrm) 
    istrm = &std::cin; 

    while(istrm->good()) 
    { 
    // reading from *istrm implemented here 
    } 
    return 0; 
} 

solo dividere le preoccupazioni in 1) capire dove di leggere e 2) la lettura effettiva:

int read(std::istream& is) 
{ 
    while(is.good()) 
    { 
    // reading from is implemented here 
    } 
    return 0; 
} 

int main(int argc, char* argv[]) 
{ 
    if(argc > 1) 
    { 
    std::ifstream ifs(argv[1]); 
    if(ifs.good()) 
     return read(ifs); 
    } 
    return read(std::cin); 
} 

devo ancora vedere un esempio reale di una variabile non era così costante come avrebbe potuto essere che non poteva essere risolto separando le preoccupazioni.

+0

+1: Devo dire che il fatto che 'const' non si applica durante l'esecuzione del ctor o del dtor già causa abbastanza confusione. –

+0

Penso che tu intenda C++ 0x, non C++ 1x. http://www2.research.att.com/~bs/C++0xFAQ.html – Ben

+0

@ Ben: l'intervallo di tempo per C++ 0x termina quest'anno e quest'anno non ci sarà un nuovo standard. Probabilmente uscirà l'anno prossimo, il che renderebbe C++ 11. Se ci sono ulteriori ritardi, sarà C++ 12. Quindi il C++ 1x. – sbi

6

Questo è un grande momento di utilizzare una funzione

#include <vector> 

std::vector<int> makeVector() 
{ 
    std::vector<int> returnValue; 
    returnValue.push_back(5); 
    return returnValue; 
} 

int main() 
{ 
    const std::vector<int> myVector = makeVector(); 
} 
+2

La tua soluzione ha una semantica diversa. L'OP vuole creare 1 'vector ' e cambiare il const non-const dell'identificatore. La tua soluzione crea 2 copie del 'vector ' con 2 leve di accesso diverse – JaredPar

+6

@Jared: l'ho letto come: L'OP vuole essenzialmente creare un vettore costante, ma non può a causa della mancanza di elenchi di inizializzatori del C++ 0x , o non avere tutti i dati contemporaneamente. – Bill

+8

@ Jared: questa versione creerà probabilmente un solo vettore (a causa dell'ottimizzazione del valore di ritorno con nome), che è possibile modificare in un unico ambito e quindi diventa costante in un altro, proprio come OP desiderato. – UncleBens

8

Stai fondamentalmente cercando di riprodurre l'effetto di un costruttore - cioè, const applica solo dopo che il costruttore completa (e solo fino a quando la dtor è invocato). In quanto tale, ciò di cui hai bisogno è un'altra classe che avvolge il tuo vettore e lo inizializza nel ctor. Una volta completato e restituito, l'istanza diventa const (presupponendo, ovviamente, che sia stato definito come const).

C++ 0x migliorerà considerevolmente il requisito per l'avvolgimento di questo tipo. Sarai in grado di utilizzare gli inizializzatori di controvento per i vettori per creare/inizializzare il vettore in un'unica operazione. Altri tipi (almeno potenzialmente) supportano gli inizializzatori definiti dall'utente per ottenere approssimativamente la stessa cosa.

7

Il C++ è staticamente digitato. Per me, introdurre una simile operazione sarebbe una violazione di questo paradigma e causerebbe molta confusione.

+2

Questa è ancora una tipizzazione statica - il compilatore può dedurre quando la variabile è const o non al momento della compilazione. – liori

+3

@liori: Forse. Se il tipo di una variabile può cambiare durante l'esecuzione, significa che dobbiamo eseguire l'analisi del percorso di esecuzione per eseguire la tipizzazione statica. Ciò non funzionerà in tutti i casi e causerà risultati sorprendenti in altri. –

+0

Ah, sì, ho dimenticato di quello. +1. – liori

3

Ho pensato anche a questo. Ma, IMHO, creerà molta confusione, che supererà i suoi benefici. Vieni a pensarci, l'intera nozione di costanza in C++ è già abbastanza confusa.

La tua idea si riduce a "Come posso rendere una variabile di sola lettura dopo che è stata inizializzata?". È possibile ottenere lo stesso effetto rendendo la variabile un membro privato di una classe, che è inizializzata nel costruttore e per la quale si fornisce un getter ma nessun setter.

3

E 'già stato detto che C++ 0x risolve questo alquanto con bretelle-inizializzatori:

const std::vector<int> values{1, 2, 3, 4, 5}; 

se questo consente solo per l'inizializzazione, e non consente, ad esempio, invocando non const funzioni membro dopo il costruttore ha eseguito. E è possibile definire una macro constify come segue:

#define constify(type, id) \ 
for (type const& id##_const(id), & id(id##_const), \ 
    * constify_index = &id; constify_index; constify_index = 0) 

che può essere utilizzato in questo modo:

std::vector<int> v; 

// v is non-const here. 

constify (std::vector<int>, v) { 

    // v is const here. 

} 

Questo funziona attraverso la creazione di un ciclo for che esegue la seguente dichiarazione o il blocco solo una volta, con la variabile confermata locale al corpo del ciclo. Si noti la dichiarazione di variabili aiutante i_const prima che il locale i: la dichiarazione int const& i(i) inizializza i- — che è, ad un valore Non inizializzato — e vogliamo che il (i) per riferirsi invece al precedenza dichiarato i, quindi un livello extra è necessario.

Se si può fare uso di C++ 0x caratteristiche, la parola chiave decltype è utile, che consente di omettere il tipo da invocazioni di constify:

#define constify(id) \ 
for (decltype(id) const& id##_const(id), & id(id##_const), \ 
    * constify_index = &id; constify_index; constify_index = 0) 

Quale permette di scrivere, semplicemente:

constify (v) { 
    // ... 
} 

Entrambe le versioni funzionano se la variabile è inizialmente dichiarata const oppure no. Quindi, sì, qualcosa di molto simile a quello che stavi cercando è effettivamente possibile, ma probabilmente non ne vale la pena.

+2

L'uso di 'const_cast' in questo modo comporta un comportamento indefinito. –

+0

Questo non è sicuro, dato che ogni volta che dichiarate una variabile come 'const', il compilatore può scegliere di metterne tutto o parte in memoria di sola lettura, portando a errori se si gira via const e si tenta di modificare l'oggetto. Questo è improbabile per una classe complessa come 'std :: vector', ma è ancora una preoccupazione. –

+0

Oh, bene. Ho detto che non era sicuro. –

3

Presumo che tu stia parlando di qualcosa di più generico di solo vettori di inizializzazione (che è risolto in C++ 0x) e utilizzare i vettori solo come esempio.

preferirei vederlo fatto attraverso un qualche tipo di funzioni locali:

const vector<int> values = []{ 
    vector<int> v; 
    copy(some_other_data.begin(), some_other_data.end(), v); 
    sort(v); 
    return v; 
}(); 

(ho potuto pasticciare funzione anonima sintassi del C++ 0x). Questo posso leggere abbastanza naturalmente come: "preparare un vettore const in base alla routine descritta qui". L'unica quantità di parentesi mi dà un po 'fastidio.

Posso vedere come questo codice potrebbe diventare un linguaggio C++ dopo che C++ 0x diventa più naturale per i programmatori.

(a cura dopo il suggerimento del dehmann)

+1

Bella risposta. E sì, sembra che tu non abbia bisogno di tutte le parentesi in '[]() {...}'. Le parentesi tonde sono opzionali, quindi diventa '[] {...}' – Frank

0

Si potrebbe avvolgere il vettore in una classe, dichiara il vettore avvolto mutevole, e poi fare un esempio const dell'involucro. La classe di avvolgimento può cambiare il vettore, ma chiamanti esterni vedere un oggetto const

2

Attualmente, const o non è qualcosa che il compilatore conosce, in modo che il compilatore non accetterà un programma che cerca di cambiare una variabile const.

Se si voleva fare un operatore di constify, si dovrebbe fare di questa una proprietà della variabile (senza ulteriori parole chiave, di ogni variabile) quindi è possibile modificare a runtime. E ovviamente dovresti lanciare un'eccezione ogni volta che un programma tenta di cambiare una variabile (attualmente) const, il che significa in effetti che ogni accesso in scrittura a ogni variabile deve prima controllare la proprietà const.

Tutto questo va contro la filosofia del C++ e ogni altro linguaggio tipizzato staticamente. E rompe anche la compatibilità binaria con le librerie esistenti.

2

Si consideri il seguente bit:

void foo(std::vector<int> & v) 
{ 
    v.push_back(1); 
    constify v; 
} 
void bar() { 
    std::vector<int> test(7); 
    foo(test); 
    test.clear(); 
} 

è la variabile v in pippo constified? È la stessa variabile di test nella barra. Pertanto, la chiamata test.clear() non dovrebbe essere valida. Penso che quello che volevi dire fosse che il nome è "consolato", non la variabile.

Sarebbe davvero banale da specificare e implementare: constify x; è una dichiarazione di un riferimento const denominato x, che ha lo stesso tipo di base della variabile x che nasconde. Segue le normali regole di ambito, tranne che può essere definito nello stesso ambito della precedente dichiarazione x.

+0

Sì, sono d'accordo è il nome che sarebbe stato consolato. Si applicherebbe sempre solo allo scope corrente. E stai suggerendo la stessa implementazione usando 'const &' come ho detto nella mia domanda. Quindi sì, il compilatore dovrebbe solo fare questa trasformazione del programma banale inserendo la variabile 'const &'. – Frank

+0

+1 È spiacevole che le risposte tardive ricevano raramente abbastanza voti per salire in cima. Molte delle altre risposte sono buone, ma questo potrebbe essere il migliore di tutti. – thb