Domanda

Perché è impossibile avere un riferimento al vuoto? L'unica cosa che ho trovato nello standard C ++ è questa riga, in 8.3.2.1

  

Un dichiaratore che specifica il tipo " riferimento a cv void " è mal formato.

Perché è così? Perché non riesco a scrivere un & Quot; generico & Quot; funzione che accetta un void&?

Giusto per essere chiari, non ho in mente un'applicazione utile in cui l'uso di un riferimento al vuoto potrebbe essere migliore rispetto all'utilizzo dei modelli, ma sono solo curioso della logica per vietare questo costrutto.


Per chiarire un po ', capisco che usando un riferimento al vuoto " come è " sarebbe insignificante come dereferenziare un puntatore-vuoto. Tuttavia, potrei lanciarlo in un riferimento a qualcosa per usarlo, no? In effetti, non vedo perché il seguente frammento possa funzionare ...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

... mentre questo non può:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
È stato utile?

Soluzione

Se avessi un riferimento al vuoto, che cosa faresti? Non sarebbe un numero, un personaggio, un puntatore o qualcosa del genere. La tua ipotetica funzione generica non può eseguire alcuna operazione su di essa, tranne prendere il suo indirizzo (e non la sua dimensione).

&

quot; & Vuoto quot; ha due usi: disconoscere qualsiasi conoscenza del tipo (come nel vuoto *) e specificare nulla invece di qualcosa (ritorno della funzione vuoto). In nessun caso è possibile dire qualcosa su un vuoto, tranne che potrebbe avere un indirizzo.

Se non riesci a pensare a un modo in cui qualcosa può essere utile, e io non posso, questa è almeno la prova che qualcosa è inutile e che potrebbe benissimo essere parte della logica qui.

Altri suggerimenti

Prima chiedi a te stesso, come rimuoveresti un puntatore vuoto?

void *p = /*something*/ ;
cout << *p << endl;

Il codice sopra riportato non ha senso, uno dei motivi per cui non abbiamo nulla è che possiamo dire " Ho bisogno di fare un lavoro generico con i puntatori qui, e non so né mi interessa cosa sto indicando quot ;. Per definizione, il compilatore non sa a cosa punta un vuoto *, quindi non può dereferenziarlo. Puoi - tramite casting - ma il compilatore no.

Un riferimento a un vuoto soffre dello stesso problema, per definizione i dati indicati non hanno un tipo, quindi non possono essere referenziati in alcun modo significativo.

Per fare riferimento a te, il programmatore, devi lanciarlo su un altro tipo, quindi puoi avere un riferimento digitato ad esso.

Non sono sicuro di averlo spiegato come volevo.

Ruben, qualche pensiero?

MODIFICA: per rispondere alla modifica.

Prendi la prima funzione, dove passi i dati void *. i dati sono un elemento perfettamente valido, puoi calcolarli o, se hai implementato alcune registrazioni, puoi registrarli.

logger << data;

e otterrai i punti dati dell'indirizzo a. Se provi a dereferenziare i dati, il compilatore ti darà un errore (al momento non hai a portata di mano il compilatore C ++, quindi non sei sicuro dell'errore reale). per esempio.

void* data = /* some assignment */;
logger << *data; // compiler error.

Ora, il compilatore non ti permetterà di dereferenziare un vuoto * per nessun motivo (non ha senso), lo stesso sta per un riferimento ai dati del vuoto &, tranne che perché è un riferimento < em> è implicitamente dereferenziato continuamente . Il compilatore non ti permetterà di dereferenziare un vuoto * su un'operazione, non ti permetterà di dereferenziarlo costantemente.

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

Non puoi fare QUALCOSA ai dati TRANNE prendere il suo indirizzo, e c'è un metodo perfettamente valido - e sicuro - integrato nel linguaggio per farlo, cioè

void* data;

Ha più senso?

Un riferimento è un riferimento a un'istanza di qualcosa. Un'istanza di qualcosa non può essere di tipo void. Ogni istanza di qualcosa deve avere un tipo specifico (e possibilmente tipi di base).

Ecco un riassunto delle diverse cose che sono state dette e che ho pensato.

Due motivi principali per cui è vietato il riferimento al vuoto


1 Sarebbero stati totalmente inutili.

In effetti, se guardiamo indietro ai tempi di C, i puntatori vuoti avevano due scopi:

  • Gestione della memoria (ad es. malloc)
  • Generalità (funzioni di scrittura che possono accettare qualsiasi tipo di argomento)

Quando è uscito C ++, i template sono diventati la migliore soluzione per implementare la genericità. Tuttavia, la gestione della memoria personalizzata doveva ancora essere possibile e l'interoperabilità tra C ++ e C era una delle principali preoccupazioni, quindi il vuoto * è stato mantenuto. Un ipotetico riferimento vuoto non sarebbe di alcun aiuto con la gestione della memoria e la generalità è già coperta, quindi sostanzialmente non avrebbe quasi alcun uso (tranne per la garanzia di non nullità descritta di seguito).

2 Non potresti farci nulla

Quando si utilizza un puntatore vuoto, non è consentito dereferenziarlo; trasposto nel caso dei riferimenti, ciò significa che non è possibile utilizzare il riferimento vuoto (sempre ipotetico). Quindi

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

Tuttavia, potremmo pensare a un caso d'uso in cui il riferimento non dovrebbe essere " dereferenziato " (questa frase è terribilmente errata, ma ottieni il mio punto), ma dove prenderemmo solo il suo indirizzo. Supponiamo che io abbia la seguente funzione:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

Per evitare questa asserzione noiosa, potrei scrivere la funzione in questo modo:

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

Tuttavia, affinché funzioni, &dataref deve essere equivalente a dataptr, che non è il caso : &*dataptr è equivalente a <=>!

Pertanto, anche prendere l'indirizzo implica una dereferenziazione, almeno concettualmente (dietro le quinte, la prima equivalenza è probabilmente vera, ma a livello semantico non lo è). Di conseguenza, non è assolutamente possibile utilizzare i dati, quindi i riferimenti vuoti sono un'aberrazione.

Tecnicamente parlando, tutto ciò che è garantito è che un riferimento a un oggetto è un alias per esso. Che il passaggio dell'argomento di riferimento della cappa sia effettuato con i puntatori è un dettaglio di implementazione. Questo può essere fonte di confusione a causa dei riferimenti che riutilizzano l'amplificatore &; operatore che è anche indirizzo di, ma tieni presente che l'operatore ha in realtà significati diversi in contesti diversi (in una dichiarazione di variabile o parametro indica un tipo di riferimento, altrimenti è indirizzo di, tranne quando è bit a bit e). Dal momento che tecnicamente è solo un alias per un oggetto, un riferimento è "sempre dereferenziato" come ha spiegato Worrier.

OK, una cosa mi infastidisce per questo. L'idea di un void*, come menzionato sopra, è che hai ancora una variabile valida contenente un indirizzo, ma il tipo viene ignorato. Ciò sembra ammissibile poiché possiamo ancora lavorare con i dati dell'indirizzo - il tipo è alquanto superfluo (o meno importante) in questo contesto. Dereferenziare è negativo, perché provare e accedere a un membro non ha senso, ad es. p.mem. Non sappiamo a quale classe fare riferimento, e quindi la memoria a cui saltare, i puntatori vtable da seguire.

Tuttavia, sembrerebbe quindi logico che p da solo sarebbe OK dato che si riferiva solo all'oggetto, ma nessuno dei suoi dati. Non sono necessarie informazioni sulla classe per farlo, solo l'indirizzo. Capisco che non serve assolutamente a questo, ma è importante definire quando le cose si guastano. Consentire questa nozione, un riferimento C ++ (costantemente senza riferimenti ma non accedendo a nulla) ad es. void& ref = static_cast< &void >(obj) ha anche senso e quindi consentirebbe riferimenti vuoti. Non sto dicendo che nessuno dovrebbe occuparsene con i responsabili, ma da un & Quot; senso & Quot; punto di vista, sembrerebbe corretto, no?

Come ha sottolineato Luc Touraille sopra (almeno, questa è la mia interpretazione), potrebbe essere implementato, ma il problema è semantico. La spiegazione ragionevole che ho potuto giungere è che poiché una variabile oggetto è un & Quot; tag & Quot; per una sequenza di memoria, il tipo ha un importante valore semantico. Pertanto, il puntatore, considerato come una variabile con un valore di indirizzo, considera il tipo un po 'superfluo, non chiave per definirlo.

Qualcuno sarebbe d'accordo con questo?

Puoi pensare a un riferimento come un puntatore de-referenziato. Sintatticamente trattate un riferimento come se non fosse un puntatore: non avete bisogno dell'operatore * per dereferenziarlo e potete usarlo. piuttosto che - > per accedere ai suoi membri.

Tuttavia, non è possibile dereferenziare un puntatore void. Come sottolineato da Binary Worrier, provare a farlo ti darà un errore del compilatore. E se non è possibile avere un puntatore vuoto non dedotto, significa che non è possibile avere un riferimento vuoto.

Se lo fossero, sarebbero semanticamente non differenziati dai puntatori e sarebbero pari allo zucchero sintattico. Un riferimento dice & Quot; Mi riferisco a qualcosa di questo tipo. & Quot; Consentire il riferimento vuoto o nullo indebolirebbe quella differenza dai puntatori.

Concesso, è ancora possibile che un riferimento faccia riferimento a un oggetto che non esiste più, ma questa è un'eccezione.

Quanto segue è non una difesa della nozione di riferimenti vuoti. Lo offro come aneddoto selvatico. Chiediti se non ha un odore divertente.

La mia azienda è stata una delle prime a utilizzare commercialmente C ++ e inizialmente compilata utilizzando Cfront . I primi sviluppatori stavano ancora imparando la lingua e in genere utilizzavano tutti i trucchi del libro (operatori ovunque!). Ecco un trucco che hanno pensato fosse bello:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

Quindi qui hai, non un riferimento vuoto, ma piuttosto un riferimento tipizzato con un legame potenzialmente vuoto ! Un momento di riflessione dovrebbe probabilmente suggerire alternative migliori a questo particolare linguaggio ...

vuoto è qualcosa che, per definizione, non esiste, quindi non è logico avere il suo indirizzo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top