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

242

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

Fred Larson
fuente

Respuestas:

216

Para devolver dos valores, uso un std::pair(generalmente typedef'd). Debería mirar boost::tuple(en C ++ 11 y más reciente, hay std::tuple) más de dos resultados de retorno.

Con la introducción de la unión estructurada en C ++ 17, el retorno std::tupleprobablemente debería convertirse en un estándar aceptado.

Robar
fuente
12
+1 para tupla. Tenga en cuenta las ramificaciones de rendimiento de los objetos grandes que regresan en una estructura frente a pasar por referencia.
Marcin
12
Si vas a usar tuplas, ¿por qué no las usas también para parejas? ¿Por qué tener un caso especial?
Ferruccio
44
Fred, sí boost :: tuple puede hacer eso :)
Johannes Schaub - litb
46
En C ++ 11, puedes usar std::tuple.
Ferruccio
14
Si desea aceptar múltiples valores de una función, una forma conveniente de hacerlo es usar std::tie stackoverflow.com/a/2573822/502144
fdermish en
176

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;
}
pepper_chico
fuente
44
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 los valores devueltos primero y segundo sin comprender la definición de la función? cociente-resto o resto-cociente.
Uchia Itachi el
77
@UchiaItachi La misma preocupación por los parámetros de la función, puede darles nombres, pero el idioma ni siquiera lo exige, y los nombres de los parámetros no tienen ningún valor en el sitio de la llamada al leer. Además, en una sola devolución, solo tiene un tipo, pero tener el nombre también podría ser útil, con tuplas simplemente duplica el problema, por lo que, de hecho, el lenguaje carece de ser auto documentado de varias maneras, no solo esto.
pepper_chico
1
¿Cómo se vería el último ejemplo si quisiera especificar explícitamente el tipo de retorno de divide ()? ¿Debo definir el resultado en otro lugar o puedo definirlo correctamente en la especificación del tipo de retorno?
Slava
1
@Slava no puede definir un tipo justo en la firma de la función, tendría que declarar el tipo fuera y usarlo como tipo de retorno, como normalmente se hace (simplemente mueva la structlínea fuera del cuerpo de la función y reemplace la autofunción de retorno result.
pepper_chico
3
@pepper_chico ¿Qué sucede si desea poner la definición de la función divideen un archivo cpp separado? Me sale el error error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. ¿Cómo puedo solucionar esto?
Adriaan
123

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

  • no siempre es obvio en la invocación qué parámetros son entradas y salidas
  • generalmente tiene que crear una variable local para capturar el resultado, mientras que los valores de retorno 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 "entrada" y una "salida" para 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 par / tupla. Principalmente, a menudo no hay un orden natural para los valores de retorno. ¿Cómo sabe 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 se aplican también 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;

¿Esto imprime velocidad y rumbo, o rumbo y velocidad? No es obvio

Compare a 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 está más claro.

Así que creo que mi primera opción en general es la técnica de estructura. La idea 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
fuente
1
La sugerencia de declarar un me structgusta Velocityes buena. Sin embargo, una preocupación es que contamina el espacio de nombres. Supongo que con C ++ 11, structpuede tener un nombre de tipo largo, y uno puede usar auto result = calculateResultingVelocity(...).
Hugues
55
+1. Una función debe devolver una "cosa", no una "tupla de cosas" ordenada de alguna manera.
DevSolar
1
Prefiero las estructuras sobre std :: pares / 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 ser utilizado como esto: auto result = my_func();. Pero C ++ no permite esto: "los nuevos tipos pueden no estar definidos en un tipo de retorno". Así que tengo que crear estructuras como struct my_func_result_t...
anton_rh
2
@anton_rh: C ++ 14 permite devolver tipos locales con auto, por lo que auto result = my_func();se puede obtener de manera trivial.
ildjarn
44
Hace unos 15 años, cuando descubrimos el impulso, utilizamos mucho la tupla, ya que es bastante útil. Con el tiempo, experimentamos la desventaja en la legibilidad, especialmente para tuplas que tienen el mismo tipo (por ejemplo, tupla <doble, doble>; 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
24
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 lista para adaptarse a dos tipos de datos.

James Curran
fuente
3
Eso funcionará para mi simple ejemplo. En general, sin embargo, puede haber más de dos valores devueltos.
Fred Larson
55
Tampoco se auto documenta. ¿Puedes recordar qué registro x86 es el resto para DIV?
Mark
1
@ Mark - Estoy de acuerdo en que las soluciones posicionales pueden ser menos mantenibles. Puede encontrarse con el problema de "permute y deflector".
Fred Larson
16

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, entonces quizás debería 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.
Stewart Johnson
fuente
1
Me gusta su respuesta y su última viñeta me recuerda algo que acabo de leer que pasar por valor se ha vuelto mucho más rápido dependiendo de las circunstancias que lo hacen más complicado ... cpp-next.com/archive/2009/08/want-speed-pass -por-valor
sabio
12

La solución OO para esto es crear una clase de razó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 permitirán 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 sea una clase completamente pensada con un constructor y algunos métodos, de hecho, el "método" que mencionó originalmente (como devolver el probablemente debería ser un miembro de esta clase que devuelva 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 muy probable que le falte un objeto.

No tengas miedo de crear estas clases pequeñas para hacer pequeños trabajos, esa es la magia de OO, terminas desglosando 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 estaba 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 accesos) es una señal de que puede que tenga que repensar algo ...

Bill K
fuente
10

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

La 'combinación de valor de retorno y parámetros de retorno' suele ser la menos limpia.

Hacer que una función devuelva un estado y devuelva datos a través de parámetros de retorno es sensato en C; es menos obvio en C ++, donde podría usar excepciones para transmitir información de fallas.

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

Jonathan Leffler
fuente
10

Con C ++ 17 también puede devolver uno o más valores inamovibles / no copiables (en ciertos casos). La posibilidad de devolver tipos inmóviles viene a través de la nueva optimización del valor de retorno garantizado, y se compone muy bien con los agregados , y 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 bonito de esto es que se garantiza que no causará ninguna copia o movimiento. Puedes hacer el ejemplomany struct también sea variable. Más detalles:

Devolución de agregados variadic (struct) y sintaxis para la 'guía de deducción de construcción' de plantilla variadic C ++ 17

Johan Lundberg
fuente
6

Hay muchas formas de devolver múltiples parámetros. Voy a estar exhausto.

Usar parámetros de referencia:

void foo( int& result, int& other_result );

usar parámetros de puntero:

void foo( int* result, int* other_result );

que tiene la ventaja de que tienes que hacer un & llamada en el sitio de la llamada, posiblemente alertando a las personas de que es un parámetro fuera de lugar.

Escribe una plantilla y úsala:

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
  out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
  out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
    target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
  template<class...Args> // TODO: SFINAE enable_if test
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X> // TODO: SFINAE enable_if test
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args> // TODO: SFINAE enable_if test
  void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};

entonces podemos hacer:

void foo( out<int> result, out<int> other_result )

Y todo está bien. fooya no puede leer ningún valor pasado como bonificación.

Se pueden utilizar otras formas de definir un lugar en el que se pueden colocar datos out. Una devolución de llamada para colocar cosas en algún lugar, por ejemplo.

Podemos devolver una estructura:

struct foo_r { int result; int other_result; };
foo_r foo();

whick funciona bien en todas las versiones de C ++, y en esto también permite:

auto&&[result, other_result]=foo();

a costo cero Los parámetros ni siquiera se pueden mover gracias a la elisión garantizada.

Podríamos devolver un std::tuple:

std::tuple<int, int> foo();

que tiene el inconveniente de que los parámetros no tienen nombre. Esto permite el:

auto&&[result, other_result]=foo();

también. Antes de en su lugar podemos hacer:

int result, other_result;
std::tie(result, other_result) = foo();

lo cual es un poco más incómodo. Sin embargo, la elisión garantizada no funciona aquí.

Al entrar en territorio extraño (¡y esto es después out<>!), Podemos usar el estilo de pase de continuación:

void foo( std::function<void(int result, int other_result)> );

y ahora las personas que llaman hacen:

foo( [&](int result, int other_result) {
  /* code */
} );

Una ventaja de este estilo es que puede devolver un número arbitrario de valores (con tipo uniforme) sin tener que administrar la memoria:

void get_all_values( std::function<void(int)> value )

la valuedevolución de llamada podría llamarse 500 veces cuando usted get_all_values( [&](int value){} ).

Por pura locura, incluso podría usar una continuación en la continuación.

void foo( std::function<void(int, std::function<void(int)>)> result );

cuyo uso se parece a:

foo( [&](int result, auto&& other){ other([&](int other){
  /* code */
}) });

lo que permitiría muchas relaciones entre resulty other.

De nuevo con los valores de California, podemos hacer esto:

void foo( std::function< void(span<int>) > results )

aquí, llamamos a la devolución de llamada con una serie de resultados. Incluso podemos hacer esto repetidamente.

Con esto, puede tener una función que pase eficientemente megabytes de datos sin hacer ninguna asignación fuera de la pila.

void foo( std::function< void(span<int>) > results ) {
  int local_buffer[1024];
  std::size_t used = 0;
  auto send_data=[&]{
    if (!used) return;
    results({ local_buffer, used });
    used = 0;
  };
  auto add_datum=[&](int x){
    local_buffer[used] = x;
    ++used;
    if (used == 1024) send_data();
  };
  auto add_data=[&](gsl::span<int const> xs) {
    for (auto x:xs) add_datum(x);
  };
  for (int i = 0; i < 7+(1<<20); ++i) {
    add_datum(i);
  }
  send_data(); // any leftover
}

Ahora, std::functiones un poco pesado para esto, ya que estaríamos haciendo esto en entornos de cero sobrecarga sin asignación. Así que querríamos un function_viewque nunca asigne.

Otra solución es:

std::function<void(std::function<void(int result, int other_result)>)> foo(int input);

donde en lugar de tomar la devolución de llamada e invocarla, en su foolugar , devuelve una función que toma la devolución de llamada.

foo (7) ([&] (int resultado, int otro_resultado) {/ * código * /}); esto rompe los parámetros de salida de los parámetros de entrada al tener corchetes separados.

Con variant ycorutinas, podría hacer fooun generador de una variante de los tipos de retorno (o solo el tipo de retorno). La sintaxis aún no está arreglada, por lo que no daré ejemplos.

En el mundo de las señales y las ranuras, una función que expone un conjunto de señales:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

le permite crear uno fooque funciona de forma asíncrona y difunde el resultado cuando finaliza.

En esta línea, tenemos una variedad de técnicas de canalización, donde una función no hace algo, sino que organiza los datos para que se conecten de alguna manera, y la acción es relativamente independiente.

foo( int_source )( int_dest1, int_dest2 );

entonces este código no hace nada hasta que int_sourcetenga números enteros para proporcionarlo. Cuando lo haga, int_dest1y int_dest2comience a recibir los resultados.

Yakk - Adam Nevraumont
fuente
¡Esta respuesta contiene más información que otras respuestas! en particular, información sobre auto&&[result, other_result]=foo();funciones que devuelven tuplas y estructuras. ¡Gracias!
jjmontes
Aprecio esta respuesta exhaustiva, especialmente porque todavía estoy atascado con C ++ 11 y, por lo tanto, no puedo usar algunas de las soluciones más modernas que otras personas proponen.
GuyGizmo
5

Use una estructura o una clase para el valor de retorno. Usar std::pairpuede funcionar por ahora, pero

  1. es inflexible si decide más tarde que desea que se le devuelva más información;
  2. No está muy claro en la declaración de la función en el encabezado qué se devuelve y en qué orden.

Es probable que devolver una estructura con nombres de variables de miembro autodocumentados sea menos propenso a errores para cualquiera que use su función. Al ponerme el sombrero de compañero de trabajo por un momento, su divide_resultestructura es fácil para mí, un usuario potencial de su función, para comprender inmediatamente después de 2 segundos. Jugar con parámetros de salida o pares y tuplas misteriosas llevaría más tiempo leerlo y puede usarse incorrectamente. Y lo más probable, incluso después de usar la función varias veces, todavía no recordaré el orden correcto de los argumentos.

Michel
fuente
4

Si su función devuelve un valor por referencia, el compilador no puede almacenarlo en un registro cuando llama a otras funciones porque, en teoría, la primera función puede guardar la dirección de la variable que se le pasa en una variable accesible globalmente, y cualquier función llamada posteriormente puede cámbielo, para que el compilador (1) guarde el valor de los registros en la memoria antes de llamar a otras funciones y (2) vuelva a leerlo cuando sea necesario de la memoria nuevamente después de cualquiera de esas llamadas.

Si regresa por referencia, la optimización de su programa sufrirá

dmityugov
fuente
4

Aquí, estoy escribiendo un programa que está devolviendo múltiples valores (más de dos valores) en c ++. Este programa es ejecutable en c ++ 14 (G ++ 4.9.2). El programa es como una calculadora.

#  include <tuple>
# include <iostream>

using namespace std; 

tuple < int,int,int,int,int >   cal(int n1, int n2)
{
    return  make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2);
}

int main()
{
    int qut,rer,add,sub,mul,a,b;
    cin>>a>>b;
    tie(qut,rer,add,sub,mul)=cal(a,b);
    cout << "quotient= "<<qut<<endl;
    cout << "remainder= "<<rer<<endl;
    cout << "addition= "<<add<<endl;
    cout << "subtraction= "<<sub<<endl;
    cout << "multiplication= "<<mul<<endl;
    return 0;
}

Por lo tanto, puede comprender claramente que de esta manera puede devolver múltiples valores de una función. usando std :: pair solo se pueden devolver 2 valores, mientras que std :: tuple puede devolver más de dos valores.

PRAFUL ANAND
fuente
44
Con C ++ 14 también puede usar el autotipo de retorno calpara hacer esto aún más limpio. (OMI)
sfjac
3

Tiendo a usar out-vals en funciones como esta, porque me apego al paradigma de una función que devuelve códigos de éxito / error y me gusta mantener las cosas uniformes.

John Dibling
fuente
2

Las alternativas incluyen matrices, generadores e inversión de control , pero ninguna es apropiada aquí.

Algunos (por ejemplo, Microsoft en Win32 histórico) tienden a usar parámetros de referencia por simplicidad, porque está claro quién asigna y cómo se verá en la pila, reduce la proliferación de estructuras y permite un valor de retorno separado para el éxito.

Los programadores "puros" prefieren la estructura, suponiendo que sea el valor de la función (como es el caso aquí), en lugar de algo que la función toca incidentalmente. Si tuviera un procedimiento más complicado, o algo con estado, probablemente usaría referencias (suponiendo que tenga una razón para no usar una clase).

marca
fuente
2

Yo diría que no hay un método preferido, todo depende de lo que va a hacer con la respuesta. Si los resultados se van a usar juntos en un procesamiento posterior, entonces las estructuras tienen sentido, si no tendré que pasarlas como referencias individuales a menos que la función se vaya a usar en una declaración compuesta:

x = divide( x, y, z ) + divide( a, b, c );

A menudo elijo pasar 'estructuras' por referencia en la lista de parámetros en lugar de tener que pasar por encima de la copia la devolución de una nueva estructura (pero esto está sudando las cosas pequeñas).

void divide(int dividend, int divisor, Answer &ans)

¿Son confusos nuestros parámetros? Un parámetro enviado como referencia sugiere que el valor va a cambiar (a diferencia de una referencia constante). El nombramiento sensible también elimina la confusión.

Patricio
fuente
1
Creo que es un poco confuso. Alguien que lee el código que lo llama ve "divide (a, b, c);". No hay indicios de que c sea un outval hasta que busquen la firma. Pero ese es un miedo general a los parámetros de referencia no constantes, más que a esta pregunta.
Steve Jessop
2

¿Por qué insiste en una función con múltiples valores de retorno? Con OOP puede usar una clase que ofrece una función regular con un único valor de retorno y cualquier cantidad de "valores de retorno" adicionales, como se muestra a continuación. La ventaja es que la persona que llama tiene la opción de mirar a los miembros de datos adicionales, pero no está obligado a hacerlo. Este es el método preferido para llamadas a redes o bases de datos complicadas, donde se puede necesitar mucha información de devolución adicional en caso de que ocurran errores.

Para responder a su pregunta original, este ejemplo tiene un método para devolver el cociente, que es lo que la mayoría de las personas que llaman pueden necesitar, y además, después de la llamada al método, puede obtener el resto como miembro de datos.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};
Roland
fuente
1
Creo que hay casos en que esto es ineficiente. Por ejemplo, tiene un solo ciclo for que genera varios valores de retorno. Si divide esos valores en funciones separadas, deberá ejecutar el ciclo una vez para cada valor.
jiggunjer
1
@jiggunjer Puede ejecutar el bucle una vez y almacenar los diversos valores de retorno en miembros de datos de clase separados. Esto subraya la flexibilidad del concepto OOP.
Roland
2

en lugar de devolver múltiples valores, simplemente devuelva uno de ellos y haga una referencia de otros en la función requerida para, por ejemplo:

int divide(int a,int b,int quo,int &rem)
Anchit Rana
fuente
¿No mencioné esto en la pregunta misma? Además, vea mis objeciones en mi respuesta .
Fred Larson
1

Boost tuple sería mi opción preferida para un sistema generalizado de devolución de más de un valor de una función.

Posible ejemplo:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}
AndyUK
fuente
1

Podemos declarar la función de modo que devuelva una variable definida por el usuario de tipo estructura o un puntero a ella. Y por la propiedad de una estructura, sabemos que una estructura en C puede contener múltiples valores de tipos asimétricos (es decir, una variable int, cuatro variables char, dos variables flotantes, etc.)

Rohit Hajare
fuente
1

Solo lo haría por referencia si solo son unos pocos valores de retorno, pero para tipos más complejos también puede hacerlo así:

static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
  return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}

use "static" para limitar el alcance del tipo de retorno a esta unidad de compilación si solo pretende ser un tipo de retorno temporal.

 SomeReturnType st = SomeFunction();
 cout << "a "   << st.a << endl;
 cout << "b "   << st.b << endl;
 cout << "c "   << st.c << endl;
 cout << "str " << st.str << endl;

Definitivamente, esta no es la forma más bonita de hacerlo, pero funcionará.

Carsten
fuente
-2

Aquí hay un ejemplo completo de este tipo de solución de problemas

#include <bits/stdc++.h>
using namespace std;
pair<int,int> solve(int brr[],int n)
{
    sort(brr,brr+n);

    return {brr[0],brr[n-1]};
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i=0; i<n; i++)
    {
        cin >> arr[i];
    }

    pair<int,int> o=solve(arr,n);
    cout << o.first << " " << o.second << endl;

    return 0;
}
Shaonsani
fuente