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

10 minutos de lectura

Devolver multiples valores de una funcion de C
Fred Larson

¿Existe una forma preferida de devolver múltiples valores desde 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 para contener todos los resultados y devolver eso:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

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

Devolver multiples valores de una funcion de C
Robar

Para devolver dos valores, uso un std::pair (normalmente tipeado). deberías mirar boost::tuple (en C++ 11 y posteriores, hay std::tuple) para más de dos resultados devueltos.

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

  • +1 para tupla. Tenga en cuenta las ramificaciones de rendimiento de los objetos grandes que regresan en una estructura frente al paso por referencia.

    – Marcin

    26 de noviembre de 2008 a las 15:40

  • Si va a usar tuplas, ¿por qué no usarlas también para pares? ¿Por qué tener un caso especial?

    – Ferruccio

    26 de noviembre de 2008 a las 15:44

  • Fred, sí boost::tuple puede hacer eso 🙂

    – Johannes Schaub – litb

    26 de noviembre de 2008 a las 15:51

  • En C++ 11, puede usar std::tuple.

    – Ferruccio

    20 de octubre de 2011 a las 10:32

  • Si desea aceptar múltiples valores de una función, una forma conveniente de hacerlo es usar std::tie stackoverflow.com/a/2573822/502144

    – fdermishin

    12 de mayo de 2014 a las 8:44

1647556814 410 Devolver multiples valores de una funcion de C
pepper_chico

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;
}

  • Tengo una preocupación con las funciones que devuelven tuplas. Digamos que el prototipo de la función anterior está en un encabezado, entonces, ¿cómo sé qué significan el primer y el segundo valor devuelto sin entender la definición de la función? cociente-resto o resto-cociente.

    – Uchia Itachi

    4 de noviembre de 2016 a las 7:22

  • @UchiaItachi La misma preocupación por los parámetros de función, puede darles nombres, pero el idioma ni siquiera lo impone, y los nombres de los parámetros no tienen valor en el sitio de llamadas cuando se leen. Además, en un solo retorno, solo tiene un tipo, pero tener el nombre también podría ser útil, con tuplas simplemente duplica el problema, por lo que, en mi opinión, el lenguaje simplemente carece de autodocumentación de varias maneras, no solo esto.

    – pepper_chico

    20 de enero de 2017 a las 15:21


  • ¿Cómo se vería el último ejemplo si quisiera especificar explícitamente el tipo de retorno de divide()? ¿Debería definir el resultado en otro lugar, o puedo definirlo directamente en la especificación del tipo de retorno?

    – Eslava

    11/07/2017 a las 13:30

  • @Slava no puede definir un tipo directamente en la firma de la función, tendría que declarar el tipo fuera y usarlo como tipo de retorno, como se hace normalmente (solo mueva el struct línea fuera del cuerpo de la función y reemplazar auto retorno de función con result.

    – pepper_chico

    11 de julio de 2017 a las 15:43


  • @pepper_chico ¿Qué pasa si quiero poner la definición de función de divide en un archivo cpp separado? me sale el error error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. ¿Cómo puedo solucionar esto?

    – Adrián

    23 de febrero de 2018 a las 20:26

Devolver multiples valores de una funcion de C
Fred Larson

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 tiene que crear una variable local para capturar el resultado, mientras que los valores devueltos se pueden usar 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 “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 de par/tupla. Principalmente, a menudo no hay un orden natural en los valores devueltos. ¿Cómo va a 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 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 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 velocidad respecto al suelo y rumbo, o rumbo y velocidad respecto al suelo? 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.

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

  • La sugerencia de declarar un struct me gusta Velocity es agradable Sin embargo, una preocupación es que contamina el espacio de nombres. Supongo que con C++11, el struct puede tener un nombre de tipo largo, y uno puede usar auto result = calculateResultingVelocity(...).

    – Hugues

    12 de febrero de 2013 a las 5:06


  • +1. Una función debe devolver una “cosa”, no una “tupla de cosas” ordenada de alguna manera.

    – DevSolar

    13 de mayo de 2013 a las 7:12

  • Prefiero estructuras sobre std::pairs/std::tuples por las razones descritas en esta respuesta. Pero tampoco me gusta el espacio de nombres “contaminación”. La solución ideal para mí sería devolver una estructura anónima como struct { int a, b; } my_func();. Esto podría usarse como esto: auto result = my_func();. Pero C ++ no permite esto: “los nuevos tipos no pueden definirse en un tipo de retorno”. Así que tengo que crear estructuras como struct my_func_result_t

    – anton_rh

    21 de abril de 2016 a las 6:14


  • @anton_rh: C++14 permite devolver tipos locales con autoentonces auto result = my_func(); es trivialmente obtenible.

    – ildjarn

    15 de enero de 2017 a las 1:11

  • Hace unos 15 años, cuando descubrimos boost, usamos mucho la tupla, ya que es bastante útil. Con el tiempo experimentamos la desventaja en la legibilidad, especialmente para las tuplas que tienen el mismo tipo (por ejemplo, tupla; cuál es cuál). Así que últimamente estamos más acostumbrados a introducir una pequeña estructura POD donde al menos el nombre de la variable miembro indica algo sensato.

    – gast128

    14 de agosto de 2017 a las 15:19

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 su solución de estructura, pero ya está definida para usted y está lista para adaptarse a dos tipos de datos cualesquiera.

Depende completamente de la función real y el significado de los valores múltiples y sus tamaños:

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

  • Me gusta tu respuesta y tu última viñeta me recuerda algo que acabo de leer de que pasar por valor se ha vuelto mucho más rápido dependiendo de las circunstancias, lo que hace que esto sea más complicado… cpp-next.com/archive/2009/08/want-speed-pass-by-value

    – sabio

    26 de diciembre de 2012 a las 19:29

1647556815 531 Devolver multiples valores de una funcion de C
proyecto de ley k

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

En realidad, creo que alguien recomendó devolver una estructura, que está lo suficientemente cerca pero oculta la intención de que esta debe ser una clase completamente pensada con un constructor y algunos métodos, de hecho, el “método” que mencionó originalmente (como devolver el pair) probablemente debería ser un miembro de esta clase que devuelve 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 debería hacer cualquier función, si desea que devuelva múltiples valores, es casi seguro que le falta un objeto.

No tenga miedo de crear estas clases diminutas para hacer pequeños trabajos, esa es la magia de OO, termina desglosá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 tiene datos; OO no se trata de pasar datos, una clase necesita administrar y manipular sus propios datos internamente, cualquier paso de datos (incluidos los accesores) es una señal de que es posible que tengas que replantearte algo.

  • Me gusta tu respuesta y tu última viñeta me recuerda algo que acabo de leer de que pasar por valor se ha vuelto mucho más rápido dependiendo de las circunstancias, lo que hace que esto sea más complicado… cpp-next.com/archive/2009/08/want-speed-pass-by-value

    – sabio

    26 de diciembre de 2012 a las 19:29

1647556816 683 Devolver multiples valores de una funcion de C
Comunidad

Con C++17 también puede devolver uno o más valores inamovibles/no copiables (en algunos casos). La posibilidad de devolver tipos inamovibles viene a través de la nueva optimización de valor de retorno garantizado, y se compone muy bien con agregadosy lo que se puede llamar constructores con plantilla.

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};

// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){
   // in place construct x,y,z with a string, 5.7 and unmovable.
   auto [x,y,z] = f();
}

Lo lindo de esto es que está garantizado que no causará ninguna copiar o mover. Puedes hacer el ejemplo many struct variadic también. Más detalles:

Devolución de agregados variádicos (estructura) y sintaxis para la plantilla variádica de C++17 ‘guía de deducción de construcción’

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad