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

Resuelto Fred Larson asked hace 16 años • 23 respuestas

¿Existe alguna forma preferida de devolver múltiples valores de una función de C++? Por ejemplo, imagine una función que divide dos números enteros y devuelve tanto el cociente como el resto. Una forma que veo comúnmente 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 que contenga todos los resultados y devolver eso:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

¿Se prefiere generalmente alguna 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.

Fred Larson avatar Nov 26 '08 22:11 Fred Larson
Aceptado

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:

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;
}
oblitum avatar May 13 '2013 06:05 oblitum

Para devolver dos valores utilizo un std::pair(normalmente tipificado). Deberías buscar boost::tuple(en C++ 11 y versiones posteriores, hay std::tuple) más de dos resultados devueltos.

Con la introducción del enlace estructurado en C++ 17, la devolución std::tupleprobablemente debería convertirse en un estándar aceptado.

Rob avatar Nov 26 '2008 15:11 Rob

Personalmente, generalmente no me gustan los parámetros de retorno por varias razones:

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

También tengo algunas reservas sobre la técnica del par/tupla. Principalmente, a menudo no existe un orden natural en los valores de retorno. ¿Cómo puede saber el lector del código si result.firstes 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, de modo 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, éste 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;

¿Imprime esto la velocidad y el rumbo, o el rumbo y la velocidad? No es obvio.

Comparar con 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.

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

Fred Larson avatar Nov 26 '2008 17:11 Fred Larson