Pregunta Devolver múltiples valores desde una función de C ++


¿Hay alguna forma de devolver valores múltiples desde una función de C ++? Por ejemplo, imagine una función que divide dos enteros y devuelve tanto el cociente como el resto. Una forma en que comúnmente veo es usar parámetros de referencia:

void divide(int dividend, int divisor, int& quotient, int& remainder);

Una variación es devolver un valor y pasar el otro a través de un parámetro de referencia:

int divide(int dividend, int divisor, int& remainder);

Otra forma sería declarar una estructura para contener todos los resultados y devolver eso:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

¿Se prefiere generalmente una de estas formas o hay otras sugerencias?

Editar: en el código del mundo real, puede haber más de dos resultados. También pueden ser de diferentes tipos.


176
2017-11-26 15:19


origen


Respuestas:


Para devolver dos valores, uso un std::pair (usualmente typedef'd). Deberías mirar boost::tuple (en C ++ 11 y más reciente, hay std::tuple) para más de dos resultados de devolución.

Con la introducción del enlace estructurado en C ++ 17, regresando std::tuple debería ser aceptado como estándar.


152
2017-11-26 15:22



Personalmente, en general no me gustan los parámetros de devolución por varias razones:

  • no siempre es obvio en la invocación qué parámetros son ins y cuáles son salidas
  • generalmente tiene que crear una variable local para capturar el resultado, mientras que los valores de retorno pueden usarse en línea (lo que puede o no ser una buena idea, pero al menos tiene la opción)
  • me parece más limpio tener una "entrada" y una "puerta de salida" a una función: todas las entradas entran aquí, todas las salidas salen
  • Me gusta mantener mis listas de argumentos lo más cortas posible

También tengo algunas reservas sobre la técnica de par / tupla. Principalmente, a menudo no hay un orden natural para los valores de retorno. ¿Cómo puede saber el lector del código si el resultado es el primero? ¿El cociente o el resto? Y el implementador podría cambiar el orden, lo que rompería el código existente. Esto es especialmente insidioso si los valores son del mismo tipo para que no se genere ningún error o advertencia del compilador. En realidad, estos argumentos también se aplican a los parámetros de retorno.

Aquí hay otro ejemplo de código, este es un poco menos trivial:

pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
                                               double planeAirspeed, double planeCourse);

pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;

¿Esto imprime velocidad y curso, o curso y velocidad terrestre? No es obvio

Compare esto:

struct Velocity {
    double speed;
    double azimuth;
};
Velocity calculateResultingVelocity(double windSpeed, double windAzimuth,
                                    double planeAirspeed, double planeCourse);

Velocity result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.speed << endl;
cout << result.azimuth << endl;

Creo que esto es más claro.

Así que creo que mi primera opción en general es la técnica de estructura. La idea de la pareja / tupla es probablemente una gran solución en ciertos casos. Me gustaría evitar los parámetros de retorno cuando sea posible.


105
2017-11-26 17:05



En C ++ 11 puedes:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  std::make_tuple(dividend / divisor, dividend % divisor);
}

#include <iostream>

int main() {
    using namespace std;

    int quotient, remainder;

    tie(quotient, remainder) = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

En C ++ 17:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

o con estructuras:

#include <tuple>

auto divide(int dividend, int divisor) {
    struct result {int quotient; int remainder;};
    return result {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto result = divide(14, 3);

    cout << result.quotient << ',' << result.remainder << endl;

    // or

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

100
2018-05-13 06:51



std::pair<int, int> divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair<int, int> answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std :: pair es esencialmente tu solución de estructura, pero ya definida para ti, y lista para adaptarse a dos tipos de datos.


21
2017-11-26 15:22



Es totalmente dependiente de la función real y el significado de los valores múltiples, y sus tamaños:

  • Si están relacionados como en tu ejemplo de fracción, entonces iría con una instancia de estructura o clase.
  • Si no están realmente relacionados y no pueden agruparse en una clase / estructura, entonces quizás deba refactorizar su método en dos.
  • Dependiendo del tamaño en memoria de los valores que está devolviendo, es posible que desee devolver un puntero a una instancia de clase o estructura, o usar parámetros de referencia.

13
2017-11-26 15:21



La solución de OO para esto es crear una clase de relación. No tomaría ningún código adicional (ahorraría algo), sería significativamente más limpio / claro, y le daría algunas refactorizaciones adicionales que le permiten limpiar el código fuera de esta clase también.

En realidad, creo que alguien recomendó devolver una estructura, que es lo suficientemente parecida pero oculta la intención de que sea una clase totalmente pensada con constructor y algunos métodos, de hecho, el "método" que mencionaste originalmente (como devolver el par) debería ser un miembro de esta clase devolviendo una instancia de sí mismo.

Sé que su ejemplo fue solo un "Ejemplo", pero el hecho es que a menos que su función esté haciendo mucho más de lo que cualquier función debería estar haciendo, si quiere que devuelva múltiples valores, es casi seguro que le falte un objeto.

No tengas miedo de crear estas pequeñas clases para hacer pequeños trabajos - esa es la magia de OO - terminas descomponiéndolo hasta que cada método sea muy pequeño y simple y cada clase pequeña y comprensible.

Otra cosa que debería haber sido un indicador de que algo andaba mal: en OO esencialmente no tienes datos - OO no se trata de pasar datos, una clase necesita administrar y manipular sus propios datos internamente, cualquier paso de datos (incluidos los accesorios) es una señal de que es posible que tengas que replantearte algo ...


11
2017-11-26 17:45



Existe un precedente para las estructuras de retorno en el estándar C (y por lo tanto C ++) con el div, ldiv (y, en C99, lldiv) funciona desde <stdlib.h> (o <cstdlib>)

La 'mezcla de valores de retorno y parámetros de retorno' suele ser la menos limpia.

Tener una función devolver un estado y devolver datos a través de parámetros de retorno es sensible en C; es menos obvio en C ++, donde podría usar excepciones para retransmitir información de fallas.

Si hay más de dos valores de retorno, entonces probablemente sea mejor un mecanismo similar a una estructura.


8
2017-11-26 17:39