Conversión implícita no permitida al regreso

21
#include <optional>

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

No compila: 'return': cannot convert from 'std::optional<int>' to 'bool'

Consulta de referencia Pensé encontrar una explicación, pero la leí, ya que debería estar bien.

Las conversiones implícitas se realizan cada vez que se usa una expresión de algún tipo T1 en un contexto que no acepta ese tipo, pero acepta algún otro tipo T2; en particular:

  • cuando la expresión se usa como argumento cuando se llama a una función que se declara con T2 como parámetro;
  • cuando la expresión se usa como un operando con un operador que espera T2;
  • al inicializar un nuevo objeto de tipo T2, incluida la instrucción return en una función que devuelve T2;
  • cuando la expresión se usa en una declaración de cambio (T2 es de tipo integral);
  • cuando la expresión se usa en una declaración if o un bucle (T2 es bool).
Darune
fuente
77
" Se realizan conversiones implícitas " , pero operator bool()de std::optionalis explicit.
Jarod42

Respuestas:

22

std::optionalno tiene ninguna facilidad para convertir implícitamente a bool. (Permitir conversiones implícitas boolgeneralmente se considera una mala idea, ya que booles un tipo integral, por lo que algo así int i = optse compilaría y haría completamente lo incorrecto).

std::optional no tener una "conversión contextual" a bool, la definición de lo que es similar a un operador de conversión: explicit operator bool(). Esto no puede usarse para conversiones implícitas; solo se aplica en ciertas situaciones específicas donde el "contexto" esperado es booleano, como la condición de una declaración if.

Lo que queremos es opt.has_value().

Sneftel
fuente
4

De documentos de C ++ :

Cuando un objeto de tipo opcional <T> se convierte contextualmente en bool, la conversión devuelve verdadero si el objeto contiene un valor y falso si no contiene un valor.

Lea sobre las conversiones contextuales aquí :

En los siguientes contextos, se espera el tipo bool y la conversión implícita se realiza si la declaración bool t (e); está bien formado (es decir, se considera una función de conversión explícita como T :: operator bool () const explícito). Dicha expresión e se dice que se convierte contextualmente en bool.

  • la expresión controladora de if, while, for;
  • los operandos de los operadores lógicos integrados!, && y ||;
  • el primer operando del operador condicional?:;
  • el predicado en una declaración static_assert;
  • la expresión en un especificador noexcept;
  • la expresión en un especificador explícito;

Puedes hacer el siguiente truco:

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

porque la conversión contextual ocurre en el caso de los operadores lógicos integrados, pero la conversión contextual no incluye returndeclaraciones y, std::optionalpor sí misma, no tiene conversión implícita a bool.

Por lo tanto, sería mejor usar el std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}
Cascanueces
fuente
¿qué pasa return {opt}? oreturn bool{opt};
darune
3
@darune return {opt};no va a funcionar, pero return static_cast<bool>(opt);o return bool{opt};funcionaría. Sin embargo, se sugiere utilizar la has_valuefunción miembro porque realmente muestra una clara intención de lo que quiere hacer
NutCracker
O el famoso return !!pot;truco ( has_valuees mejor)
LF
1

Esto se debe a que la cobertura implícita de std :: opcional para bool no es compatible: https://en.cppreference.com/w/cpp/utility/optional/operator_bool

constexpr operador explícito bool () const noexcept;

Debe convertir explícitamente a bool como bool(opt)o simplemente usar opt.has_value()en su lugar.

theWiseBro
fuente
bool {opt} también funciona y debería preferirse a bool (opt)
darune
1

Esto no se trata realmente de conversión implícita, se trata del tipo de inicialización.

Lo que tiene opcional es una función de conversión explícita, es decir

explicit operator bool() const; 

Desde N4849 [class.conv.fct] / p2

Una función de conversión puede ser explícita (9.2.2), en cuyo caso solo se considera como una conversión definida por el usuario para la inicialización directa.

Lo anterior significa que estos casos utilizarán la función de conversión: [dcl.init] / p16

La inicialización que se produce (16.1) - para un inicializador que es una lista de expresión entre paréntesis o una lista de inicialización entre paréntesis, (16.2) - para un nuevo inicializador (7.6.2.7), (16.3) - en una expresión static_cast ( 7.6.1.8), (16.4) - en una conversión de tipo de notación funcional (7.6.1.3), y (16.5) - en la forma de lista de inicialización arriostrada de una condición se llama inicialización directa.

Sin embargo, estos casos no utilizarán la función de conversión: [dcl.init] / p15

La inicialización que ocurre en la forma = de un inicializador o condición de paréntesis o igual (8.5), así como en el paso de argumentos, el retorno de funciones, el lanzamiento de una excepción (14.2), el manejo de una excepción (14.4) y la inicialización del miembro (9.4.1), se llama copia-inicialización.

El ejemplo en la pregunta corresponde al caso de inicialización de copia y no utiliza la función de conversión opcional.

Trixie
fuente