¿Función con un tipo de devolución personalizado y las condiciones de devolución falsas?

¿Función con un tipo de devolución personalizado y las condiciones de devolución falsas?

Hay tres enfoques generales:

  • Utilice excepciones. Esto es lo que está en la respuesta de Betsabé.
  • Regresar std::optional<Cell> (o algún otro tipo que puede o no tener un Cell real ).
  • Devolver bool y agrega un Cell & parámetro.

Cuál de estos es mejor depende de cómo pretenda usar esta función. Si el caso de uso principal es pasar un segmento válido, entonces, por supuesto, use excepciones.

Si parte del diseño de esta función es que se puede usar para saber si un segmento es válido, las excepciones no son apropiadas y mi opción preferida sería std::optional<Cell> . Es posible que esto aún no esté disponible en la implementación de su biblioteca estándar (es una característica de C++ 17); si no, boost::optional<Cell> puede ser útil (como se menciona en la respuesta de Richard Hodges).

En los comentarios, en lugar de std::optional<Cell> , usuario Usted sugirió expected<Cell, error> (no estándar de C++, pero propuesto para un futuro estándar e implementable fuera del std espacio de nombres hasta entonces). Esta puede ser una buena opción para agregar alguna indicación sobre por qué no Cell se pudo encontrar para el segment parámetro pasado, si hay varias razones posibles.

La tercera opción la incluyo principalmente para completar. No lo recomiendo. Es un patrón popular y generalmente bueno en otros idiomas.


¿Es esta función una consulta, que podría no encontrar la celda válidamente, o es un imperativo, donde se espera encontrar la celda?

Si es lo primero, devuelve un puntero opcional (o que admite valores NULL) a la celda.

Si es lo último, lanzar una excepción si no se encuentra.

Anterior:

boost::optional<Cell> CSV::Find(std::string segment) {
  boost::optional<Cell> result;
  // Search code here.
  return result;
}

Último:como lo tienes.

Y, por supuesto, está el enfoque basado en variantes de C++17:

#include <variant>
#include <string>

struct CellNotFound {};
struct Cell {};

using CellFindResult = std::variant<CellNotFound, Cell>;


CellFindResult Find(std::string segment) {
  CellFindResult result { CellNotFound {} };

  // Search code here.
  return result;
}

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

void cellsAndStuff()
{
    std::visit(overloaded
    {
        [&](CellNotFound)
        {
            // the not-found code
        },
        [&](Cell c)
        {
            // code on cell found
        }
    }, Find("foo"));
}

La forma en que C++ trata con fallas abyectas es definir una clase de excepción de la forma:

struct CSVException : std::exception{};

En su función entonces throw uno de esos en la rama de falla:

Cell CSV::Find(std::string segment) {
  Cell result;
  // Search code here.
  if (fail) throw CSVException();
  return result;
}

Luego maneja el caso fallido con un try catch bloquear en el sitio de llamada.

Sin embargo, si la rama "fallida" es un comportamiento normal (subjetivo, pero solo usted puede ser el juez de la normalidad), entonces imbuya algún tipo de indicador de falla dentro de Cell , o incluso cambiar el tipo de retorno a std::optional<Cell> .