Devolver valor opcional con ?: operador

3 minutos de lectura

Avatar de usuario de MiP
MiP

A menudo necesito usar el tipo opcional para las funciones:

std::optional<int32_t> get(const std::string& field)
{
    auto it = map.find(field);
    if (it != map.end()) return it->second;
    return {};
}

¿Hay alguna forma de devolver un valor opcional en una línea? por ejemplo, esto:

std::optional<int32_t> get(const std::string& field)
{
    auto it = map.find(field);
    return it != map.end() ? it->second : {};
}

resulta en el error

error: expected primary-expression before '{' token
return it != map.end() ? it->second : {};
                                      ^

  • @tobi303 {} no se analizó como una expresión.

    – MiP

    29 de julio de 2017 a las 13:36

  • Clang da un mensaje de error mucho más agradable: “la lista de inicializadores no se puede usar en el lado derecho del operador ‘?”

    – Emlai

    29 de julio de 2017 a las 14:38


avatar de usuario de dfrib
dfrib

Puede envolver explícitamente el retorno de algún valor en un std::optionaly recurre a la constexpr std::nullopt por la devolución sin valor.

std::nullopt:

std::nullopt es una constante de tipo std::nullopt_t que se utiliza para indicar un tipo opcional con estado no inicializado.

std::nullopt_t:

std::nullopt_t es un tipo de clase vacío que se usa para indicar un tipo opcional con estado no inicializado. En particular, std::optional tiene un constructor con nullopt_t como un solo argumento, lo que crea un opcional que no contiene un valor.

Con este enfoque, la cláusula true de la llamada al operador ternario devuelve explícitamente un std::optional con algún valor, para que el compilador pueda deducir el parámetro de plantilla/tipo envuelto (en este ejemplo: int32_t) del tipo del valor envuelto proporcionado, lo que significa que no necesita especificarlo explícitamente.

Aplicado a tu ejemplo:

return it != map.end() ? std::optional(it->second) : std::nullopt;

// alternatively
return it != map.end() ? std::make_optional(it->second) : std::nullopt;

Avatar de usuario de Baum mit Augen
Baum mit Augen

return it != map.end() ? it->second : std::optional<int32_t>{};

debería hacer el truco.

El compilador debe deducir el tipo de resultado de la expresión ternaria de los dos últimos operandos, pero no hay forma de que pueda deducir std::optional<int32_t> de int32_t y {}.

int32_t y std::optional<int32_t> por otro lado, tienen el tipo común deseado std::optional<int32_t>.


Dato curioso relacionado: puede evitar repetir el tipo con la deducción del tipo de retorno automático:

auto get(const std::string& field)
{
    auto it = map.find(field);
    return it != map.end() ? it->second : std::optional<int32_t>{};
}

Dependiendo de la preferencia, por supuesto, también puede inferir el argumento de la plantilla para el std::optional de it->second con decltype para reducir aún más la repetición.

  • dependiendo de cómo map se declara (y la medida en que int32_t plausiblemente podría variar en el momento de la compilación), podría ir aún más lejos y eliminarlo con una plantilla.

    –Kevin

    29 de julio de 2017 a las 18:43

¿Ha sido útil esta solución?