Conversione implicita non consentita al ritorno


21
#include <optional>

bool f() {
  std::optional<int> opt;
  return opt;
}

Non compilare: 'return': cannot convert from 'std::optional<int>' to 'bool'

Consultazione di riferimento Avrei pensato di trovare una spiegazione, ma l'ho letto come dovrebbe essere ok.

Le conversioni implicite vengono eseguite ogni volta che nel contesto viene utilizzata un'espressione di un tipo T1 che non accetta quel tipo, ma accetta qualche altro tipo T2; in particolare:

  • quando l'espressione viene utilizzata come argomento quando si chiama una funzione dichiarata con T2 come parametro;
  • quando l'espressione viene utilizzata come operando con un operatore che prevede T2;
  • quando si inizializza un nuovo oggetto di tipo T2, inclusa l'istruzione return in una funzione che restituisce T2;
  • quando l'espressione viene utilizzata in un'istruzione switch (T2 è di tipo integrale);
  • quando l'espressione viene utilizzata in un'istruzione if o in un ciclo (T2 è bool).

7
" Vengono eseguite conversioni implicite " , ma operator bool()di std::optionalis explicit.
Jarod42,

Risposte:


22

std::optionalnon ha alcuna possibilità di conversione implicita in bool. (Consentire conversioni implicite boolè generalmente considerato una cattiva idea, poiché boolè un tipo integrale quindi qualcosa di simile int i = optsi compila e fa completamente la cosa sbagliata.)

std::optional non avere una "conversione contestuale" per bool, la cui definizione sembra simile ad un operatore di cast: explicit operator bool(). Questo non può essere utilizzato per conversioni implicite; si applica solo in determinate situazioni specifiche in cui il "contesto" atteso è booleano, come la condizione di un'istruzione if.

Quello che vuoi è opt.has_value().


4

Dai documenti C ++ :

Quando un oggetto di tipo opzionale <T> viene convertito contestualmente in bool, la conversione restituisce true se l'oggetto contiene un valore e false se non contiene un valore.

Leggi le conversioni contestuali qui :

Nei seguenti contesti, è previsto il tipo bool e la conversione implicita viene eseguita se la dichiarazione bool t (e); è ben formato (ovvero viene considerata una funzione di conversione esplicita come esplicita T :: operator bool () const;). Si dice che tale espressione e sia contestualmente convertita in bool.

  • l'espressione di controllo di if, while, for;
  • gli operandi degli operatori logici integrati!, && e ||;
  • il primo operando dell'operatore condizionale?:;
  • il predicato in una dichiarazione static_assert;
  • l'espressione in un identificatore noexcept;
  • l'espressione in un identificatore esplicito;

Puoi fare il seguente hack:

bool f() {
    std::optional<int> opt;
    return opt || false;
}

perché la conversione contestuale avviene nel caso degli operatori logici incorporati, ma la conversione contestuale non include returnistruzioni e std::optionalda sola non ha una conversione implicita in bool.

Pertanto, sarebbe meglio usare std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}

che dire return {opt}? oppurereturn bool{opt};
martedì

3
@darune return {opt};non funzionerà, ma return static_cast<bool>(opt);o return bool{opt};funzionerebbe. Tuttavia, si suggerisce di utilizzare la has_valuefunzione membro perché mostra davvero la chiara intenzione di ciò che si desidera fare
NutCracker

O il famoso return !!pot;hack ( has_valueè meglio)
LF


1

Non si tratta in realtà della conversione implicita, si tratta del tipo di inizializzazione.

Ciò che è facoltativo è una funzione di conversione esplicita, ad es

explicit operator bool() const; 

Da N4849 [class.conv.fct] / p2

Una funzione di conversione può essere esplicita (9.2.2), nel qual caso viene considerata solo una conversione definita dall'utente per l'inizializzazione diretta.

Quanto sopra indica che questi casi utilizzeranno la funzione di conversione: [dcl.init] / p16

L'inizializzazione che si verifica (16.1) - per un inizializzatore che è un elenco di espressioni tra parentesi o un elenco di avvio rinforzato, (16.2) - per un nuovo inizializzatore (7.6.2.7), (16.3) - in un'espressione static_cast ( 7.6.1.8), (16.4) - in una conversione del tipo di notazione funzionale (7.6.1.3), e (16.5) - nella forma di lista di avvio rinforzata di una condizione è chiamata inizializzazione diretta.

Tuttavia, in questi casi non verrà utilizzata la funzione di conversione: [dcl.init] / p15

L'inizializzazione che si verifica nella forma = di un inizializzatore o condizione di parentesi graffa o uguale (8.5), nonché nel passaggio degli argomenti, nel ritorno di funzione, nel lanciare un'eccezione (14.2), nella gestione di un'eccezione (14.4) e nell'inizializzazione del membro (9.4.1), si chiama inizializzazione della copia.

L'esempio nella domanda rientra nel caso di inizializzazione della copia e non utilizza la funzione di conversione di optional.

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.