Devolver múltiples valores de una función C++
¿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.
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;
}
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::tuple
probablemente debería convertirse en un estándar aceptado.
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.first
es 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.