Perché le funzioni eliminate da C ++ 11 partecipano alla risoluzione dell'overload?


Risposte:


114

Metà dello scopo della = deletesintassi è 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 deleterimossa completamente la funzione, ciò renderebbe la = deletesintassi 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 deletequelli 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 deletefunzione d.

= deletesignifica "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 = deletesignifica 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 = deletesintassi è 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.


1
@ Mehrdad: Se hai bisogno di maggiori chiarimenti sul perché = delete non funziona nel modo desiderato, devi essere più chiaro sulla semantica esatta che pensi che = delete dovrebbe avere. Dovrebbe essere "fingere di non aver mai scritto questa riga?" O dovrebbe essere qualcos'altro?
Nicol Bolas

1
No, voglio dire che mi aspettavo = deleteche "questo membro non esiste", il che implicherebbe che non può partecipare alla risoluzione del sovraccarico.
user541686

6
@ Mehrdad: E questo risale al mio punto originale, motivo per cui l'ho pubblicato: se = deletesignificasse "questo membro non esiste", il primo esempio che ho pubblicato non sarebbe in grado di impedire alle persone di passare numeri interi al onlydoublecostruttore di , poiché il onlydoublesovraccarico 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 = deletesintassi: essere in grado di dire: "Non puoi passare X implicitamente a questa funzione".
Nicol Bolas

3
@ Mehrdad: Secondo questa logica, perché ne hai bisogno =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 = deletesintassi ci permette di fare qualcosa che prima era altamente scomodo e imperscrutabile in un modo molto più ovvio e ragionevole.
Nicol Bolas

2
@Mehrdad: Perché quest'ultimo è possibile : si chiama "non dichiararlo". Allora non esisterà. Quanto al motivo per cui abbiamo bisogno della sintassi per nascondere un sovraccarico piuttosto che abusare del privato ... ti stai davvero chiedendo perché dovremmo avere un mezzo per affermare esplicitamente qualcosa, piuttosto che abusare di qualche altra caratteristica per ottenere lo stesso effetto ? È semplicemente più ovvio per chiunque legga il codice cosa sta succedendo. Rende il codice più facilmente comprensibile all'utente e facilita la scrittura, risolvendo anche i problemi nella soluzione alternativa. Non abbiamo nemmeno bisogno di lambda.
Nicol Bolas

10

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 ]


4
La logica sembra molto chiara dagli esempi, non credi?
Jesse Good
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.