Perché C ++ 11 fa partecipare le funzioni " delete
d" alla risoluzione dell'overload ?
Perché è utile? O in altre parole, perché sono nascosti invece di essere cancellati completamente?
Perché C ++ 11 fa partecipare le funzioni " delete
d" alla risoluzione dell'overload ?
Perché è utile? O in altre parole, perché sono nascosti invece di essere cancellati completamente?
Risposte:
Metà dello scopo della = delete
sintassi è impedire alle persone di chiamare determinate funzioni con determinati parametri. Questo è principalmente per prevenire conversioni implicite in determinati scenari specifici. Per impedire un sovraccarico particolare, deve partecipare alla risoluzione del sovraccarico.
La risposta che citi ti dà un esempio perfetto:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Se delete
rimossa completamente la funzione, ciò renderebbe la = delete
sintassi equivalente a questa:
struct onlydouble2 {
onlydouble2(double);
};
Potresti farlo:
onlydouble2 val(20);
Questo è C ++ legale. Il compilatore esaminerà tutti i costruttori; nessuno di loro accetta direttamente un tipo intero. Ma uno di loro può prenderlo dopo una conversione implicita. Quindi lo chiameremo.
onlydouble val(20);
Questo non è C ++ legale. Il compilatore esaminerà tutti i costruttori, inclusi delete
quelli d. Vedrà una corrispondenza esatta, tramite std::intmax_t
(che corrisponderà esattamente a qualsiasi valore letterale intero). Quindi il compilatore lo selezionerà e quindi emetterà immediatamente un errore, perché ha selezionato una delete
funzione d.
= delete
significa "Lo proibisco", non semplicemente "Questo non esiste". È un'affermazione molto più forte.
Stavo chiedendo perché lo standard C ++ dice = delete significa "lo vieto" invece di "questo non esiste"
È perché non abbiamo bisogno di una grammatica speciale per dire "questo non esiste". Otteniamo questo implicitamente semplicemente non dichiarando il particolare "questo" in questione. "Lo proibisco" rappresenta un costrutto che non può essere realizzato senza una grammatica speciale. Quindi otteniamo una grammatica speciale per dire "Lo proibisco" e non l'altra cosa.
L'unica funzionalità che si otterrebbe con una grammatica esplicita "questo non esiste" sarebbe impedire a qualcuno di dichiararla in seguito. E questo non è abbastanza utile da aver bisogno della sua grammatica.
altrimenti non è possibile dichiarare che il costruttore di copia non esiste e la sua esistenza può causare ambiguità prive di senso.
Il costruttore di copia è una funzione membro speciale. Ogni classe ha sempre un costruttore di copia. Proprio come hanno sempre un operatore di assegnazione della copia, un costruttore di spostamenti, ecc.
Queste funzioni esistono; la domanda è solo se sia legale chiamarli. Se si prova a dire che = delete
significa che non esistono, la specifica dovrebbe spiegare cosa significa che una funzione non esiste. Questo non è un concetto gestito dalla specifica.
Se si tenta di chiamare una funzione che non è stata ancora dichiarata / definita, il compilatore darà un errore. Ma si verificherà un errore a causa di un identificatore indefinito , non a causa di un errore "la funzione non esiste" (anche se il compilatore lo segnala in questo modo). Vari costruttori sono tutti chiamati dalla risoluzione dell'overload, quindi la loro "esistenza" viene gestita a tale riguardo.
In ogni caso, esiste una funzione dichiarata tramite identificatore o un costruttore / distruttore (anch'esso dichiarato tramite identificatore, solo un identificatore di tipo). Il sovraccarico degli operatori nasconde l'identificatore dietro lo zucchero sintattico, ma è ancora lì.
La specifica C ++ non può gestire il concetto di una "funzione che non esiste". Può gestire una mancata corrispondenza di sovraccarico. Può gestire un'ambiguità di sovraccarico. Ma non sa cosa non c'è. Quindi = delete
è definito in termini di "tentativi di chiamare questo fallimento" molto più utili piuttosto che di "fingere di non aver mai scritto questa riga".
E ancora, rileggi la prima parte. Non puoi farlo con "la funzione non esiste". Questo è un altro motivo per cui è definito in questo modo: perché uno dei principali casi d'uso della = delete
sintassi è quello di essere in grado di costringere l'utente a utilizzare determinati tipi di parametri, a eseguire il cast esplicito e così via. Fondamentalmente, per sventare le conversioni di tipo implicite.
Il tuo suggerimento non lo farebbe.
= delete
che "questo membro non esiste", il che implicherebbe che non può partecipare alla risoluzione del sovraccarico.
= delete
significasse "questo membro non esiste", il primo esempio che ho pubblicato non sarebbe in grado di impedire alle persone di passare numeri interi al onlydouble
costruttore di , poiché il onlydouble
sovraccarico che viene eliminato non esisterebbe . Non parteciperebbe alla risoluzione del sovraccarico e quindi non ti impedirebbe di passare numeri interi. Che è la metà del punto della = delete
sintassi: essere in grado di dire: "Non puoi passare X implicitamente a questa funzione".
=delete
? Dopotutto, possiamo dire "non copiabili" facendo la stessa identica cosa: dichiarando privato il costruttore / compito della copia. Inoltre, nota che dichiarare qualcosa di privato non lo rende non cancellabile; il codice all'interno della classe può ancora chiamarlo. Quindi non è la stessa cosa di = delete
. No, la = delete
sintassi ci permette di fare qualcosa che prima era altamente scomodo e imperscrutabile in un modo molto più ovvio e ragionevole.
Il C ++ Working Draft 2012-11-02 non fornisce una logica alla base di questa regola, solo alcuni esempi
8.4.3 Definizioni eliminate [dcl.fct.def.delete]
...
3 [ Esempio : è possibile applicare l'inizializzazione non predefinita e l'inizializzazione non integrale con
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- esempio finale ]
[ Esempio : si può impedire l'uso di una classe in certe nuove espressioni utilizzando definizioni cancellate di un operatore dichiarato dall'utente nuovo per quella classe.
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
- esempio finale ]
[ Esempio : si può rendere una classe non copibile, cioè solo per lo spostamento, utilizzando le definizioni eliminate del costruttore della copia e dell'operatore di assegnazione della copia, e quindi fornendo definizioni predefinite del costruttore dello spostamento e dell'operatore di assegnazione dello spostamento.
struct moveonly {
moveonly() = default;
moveonly(const moveonly&) = delete;
moveonly(moveonly&&) = default;
moveonly& operator=(const moveonly&) = delete;
moveonly& operator=(moveonly&&) = default;
~moveonly() = default;
};
moveonly *p;
moveonly q(*p); // error, deleted copy constructor
- esempio finale ]