Retornando vários valores de uma função C ++


242

Existe uma maneira preferida de retornar vários valores de uma função C ++? Por exemplo, imagine uma função que divida dois números inteiros e retorne o quociente e o restante. Uma maneira que geralmente vejo é usar parâmetros de referência:

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

Uma variação é retornar um valor e passar o outro por um parâmetro de referência:

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

Outra maneira seria declarar uma estrutura para conter todos os resultados e retornar que:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

Uma dessas maneiras geralmente é preferida ou existem outras sugestões?

Editar: no código do mundo real, pode haver mais de dois resultados. Eles também podem ser de tipos diferentes.

Respostas:


216

Para retornar dois valores, eu uso um std::pair(normalmente digitado). Você deve procurar boost::tuple(no C ++ 11 e mais recente, existe std::tuple) mais de dois resultados de retorno.

Com a introdução da ligação estruturada no C ++ 17, o retorno std::tupleprovavelmente deve se tornar padrão aceito.


12
+1 para tupla. Lembre-se das ramificações de desempenho de objetos grandes retornando em uma estrutura versus passando por referência.
Marcin

12
Se você for usar tuplas, por que não usá-las também em pares. Por que ter um caso especial?
Ferruccio

4
Fred, sim boost :: tuple pode fazer isso :)
Johannes Schaub - litb

46
No C ++ 11, você pode usar std::tuple.
Ferruccio

14
Se você quiser aceitar vários valores de uma função, uma maneira conveniente de fazer isso é usar std::tie stackoverflow.com/a/2573822/502144
fdermishin

175

No C ++ 11, você pode:

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

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

ou com estruturas:

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

4
Eu tenho uma preocupação com as funções que retornam tuplas. Digamos que o protótipo da função acima esteja em um cabeçalho, então como sei o que o primeiro e o segundo valores retornados significam sem entender a definição da função? quociente-restante ou quociente-restante.
amigos estão dizendo sobre uchia itachi

7
@UchiaItachi A mesma preocupação com os parâmetros de função, você pode atribuir nomes a eles, mas o idioma nem impõe isso, e os nomes dos parâmetros não têm valor no site de chamada durante a leitura. Além disso, em um único retorno, você apenas tem um tipo, mas ter o nome também pode ser útil. Com as tuplas, você apenas duplica o problema. Portanto, o idioma carece de ser auto-documentado de várias maneiras, e não apenas isso.
pepper_chico

1
como seria o último exemplo se eu quisesse especificar explicitamente o tipo de retorno de divide ()? Devo definir o resultado em outro lugar ou posso defini-lo na especificação do tipo de retorno?
Slava

1
@Slava, você não pode definir um tipo diretamente na assinatura da função, teria que declarar o tipo fora e usá-lo como tipo de retorno, como normalmente é feito (basta mover a structlinha para fora do corpo da função e substituir o autoretorno da função por result.
pepper_chico 11/07

3
@pepper_chico E se você quiser colocar a definição de função divideem um arquivo cpp separado? Eu recebo o erro error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Como eu resolvo isso?
Adriaan

123

Pessoalmente, geralmente não gosto de parâmetros de retorno por vários motivos:

  • nem sempre é óbvio na invocação quais parâmetros estão dentro e quais estão fora
  • você geralmente precisa criar uma variável local para obter o resultado, enquanto os valores de retorno podem ser usados ​​em linha (o que pode ou não ser uma boa ideia, mas pelo menos você tem a opção)
  • parece mais limpo para mim ter uma "entrada" e uma "saída externa" para uma função - todas as entradas entram aqui, todas as saídas saem por aí
  • Eu gosto de manter minhas listas de argumentos o mais curtas possível

Também tenho algumas reservas sobre a técnica de par / tupla. Principalmente, geralmente não há ordem natural para os valores retornados. Como o leitor do código pode saber se result.first é o quociente ou o restante? E o implementador poderia alterar a ordem, o que quebraria o código existente. Isso é especialmente insidioso se os valores forem do mesmo tipo, para que nenhum erro ou aviso do compilador seja gerado. Na verdade, esses argumentos também se aplicam aos parâmetros de retorno.

Aqui está outro exemplo de código, este um pouco 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;

Isso imprime velocidade e percurso no solo, ou percurso e velocidade no solo? Não é óbvio.

Compare com isso:

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;

Eu acho que isso é mais claro.

Então eu acho que minha primeira escolha em geral é a técnica struct. A ideia de par / tupla provavelmente é uma ótima solução em certos casos. Eu gostaria de evitar os parâmetros de retorno sempre que possível.


1
A sugestão de declarar um structlike Velocityé boa. No entanto, uma preocupação é que polua o espaço para nome. Suponho que, com C ++ 11, o structpode ter um nome de tipo longo e um possa ser usado auto result = calculateResultingVelocity(...).
Hugues

5
+1. Uma função deve retornar uma "coisa", não uma "tupla de coisas" de alguma forma ordenada.
DevSolar 13/05

1
Eu prefiro estruturas sobre std :: pairs / std :: tuplas pelos motivos descritos nesta resposta. Mas não gosto do espaço de nomes "poluição" também. A solução ideal para mim seria retornar uma estrutura anônima como struct { int a, b; } my_func();. Isto poderia ser usado como este: auto result = my_func();. Mas o C ++ não permite isso: "novos tipos podem não ser definidos em um tipo de retorno". Então eu tenho que criar estruturas como struct my_func_result_t...
anton_rh

2
@anton_rh: C ++ 14 permite retornar tipos locais com auto, portanto auto result = my_func();é trivialmente obtido.
precisa saber é o seguinte

4
Há cerca de 15 anos, quando descobrimos o impulso, usamos muito a tupla, pois é bastante útil. Ao longo do tempo, experimentamos a desvantagem na legibilidade, especialmente para as tuplas do mesmo tipo (por exemplo, tupla <dupla, dupla>; qual é qual). Ultimamente, temos mais o hábito de introduzir uma pequena estrutura de POD em que pelo menos o nome da variável membro indica algo sensato.
precisa saber é

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 é essencialmente sua solução struct, mas já definida para você e pronta para se adaptar a dois tipos de dados.


3
Isso funcionará para o meu exemplo simples. Em geral, no entanto, pode haver mais de dois valores retornados.
22711 Fred Larson

5
Também não é auto-documentado. Você consegue se lembrar qual registro x86 é o restante para o DIV?
Mark

1
@ Mark - Concordo que as soluções posicionais podem ser menos sustentáveis. Você pode se deparar com o problema "permutar e confundir".
22811 Fred Larson

16

É totalmente dependente da função real e do significado dos vários valores e seus tamanhos:

  • Se eles estão relacionados como no seu exemplo de fração, eu usaria uma instância de struct ou de classe.
  • Se eles não estão realmente relacionados e não podem ser agrupados em uma classe / estrutura, talvez você deva refatorar seu método em dois.
  • Dependendo do tamanho dos valores retornados na memória, convém retornar um ponteiro para uma instância ou estrutura de classe ou usar parâmetros de referência.

1
Gosto da sua resposta e seu último marcador me lembra algo que acabei de ler que a transferência de valor ficou muito mais rápida, dependendo das circunstâncias que tornam isso mais complicado ... cpp-next.com/archive/2009/08/want-speed-pass -by-value
sábio

12

A solução OO para isso é criar uma classe de proporção. Não seria necessário nenhum código extra (economizaria alguns), seria significativamente mais limpo / claro e forneceria algumas refatorações extras, permitindo que você limpasse também o código fora desta classe.

Na verdade, acho que alguém recomendou o retorno de uma estrutura, que é próxima o suficiente, mas esconde a intenção de que essa seja uma classe totalmente pensada com construtor e alguns métodos, de fato, o "método" que você mencionou originalmente (como retornando o par) provavelmente deve ser um membro dessa classe retornando uma instância de si mesma.

Eu sei que o seu exemplo foi apenas um "Exemplo", mas o fato é que, a menos que sua função esteja fazendo muito mais do que qualquer função deveria fazer, se você deseja que ele retorne vários valores, certamente está perdendo um objeto.

Não tenha medo de criar essas classes minúsculas para fazer pequenos trabalhos - essa é a mágica do OO - você acaba dividindo-o até que todo método seja muito pequeno e simples e que toda classe seja pequena e compreensível.

Outra coisa que deveria ter sido um indicador de que algo estava errado: no OO você basicamente não tem dados - OO não se trata de repassar dados, uma classe precisa gerenciar e manipular seus próprios dados internamente, qualquer passagem de dados (incluindo acessadores) é um sinal de que você pode precisar repensar algo ..


10

Existe um precedente para voltar estruturas em C (e, portanto, C ++) padrão com a div, ldiv(e, em C99, lldiv) a partir de funções <stdlib.h>(ou <cstdlib>).

A 'combinação de valor de retorno e parâmetros de retorno' é geralmente a menos limpa.

Ter uma função retornando um status e retornando dados através de parâmetros de retorno é sensato em C; é menos óbvio no C ++, onde você pode usar exceções para retransmitir informações de falha.

Se houver mais de dois valores de retorno, um mecanismo semelhante à estrutura é provavelmente o melhor.


10

Com o C ++ 17, você também pode retornar um ou mais valores imóveis / não copiáveis (em certos casos). A possibilidade de retornar tipos imóveis vem da nova otimização do valor de retorno garantido e compõe-se muito bem com agregados e o que pode ser chamado de construtores de modelo .

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

A coisa bonita sobre isso é que é garantido que não causa qualquer copiar ou mover. Você também pode tornar o exemplo manystruct variadic. Mais detalhes:

Retornando agregados variadicos (struct) e sintaxe para o modelo variadico C ++ 17 'guia de dedução de construção'


6

Existem várias maneiras de retornar vários parâmetros. Eu vou ser exhastive.

Use parâmetros de referência:

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

use os parâmetros do ponteiro:

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

que tem a vantagem de fazer um &no site de chamada, possivelmente alertando as pessoas de que é um parâmetro externo.

Escreva um modelo e use-o:

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

então nós podemos fazer:

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

e tudo está bem. foonão é mais capaz de ler qualquer valor transmitido como um bônus.

Outras formas de definir um ponto que você pode colocar dados podem ser usadas para construir out. Um retorno de chamada para substituir as coisas em algum lugar, por exemplo.

Podemos retornar uma estrutura:

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

whick funciona bem em todas as versões do C ++ e no isso também permite:

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

a custo zero. Os parâmetros nem sequer podem ser movidos graças à garantia garantida.

Poderíamos retornar um std::tuple:

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

que tem a desvantagem de que os parâmetros não são nomeados. Isso permite que o:

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

também. Antes de em vez disso, podemos fazer:

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

o que é um pouco mais estranho. A elisão garantida não funciona aqui, no entanto.

Indo para um território mais estranho (e isso é depois out<>!), Podemos usar o estilo de passagem de continuação:

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

e agora os chamadores fazem:

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

Um benefício desse estilo é que você pode retornar um número arbitrário de valores (com tipo uniforme) sem precisar gerenciar a memória:

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

o valueretorno de chamada pode ser chamado 500 vezes quando você get_all_values( [&](int value){} ).

Por pura insanidade, você pode até usar uma continuação na continuação.

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

cujo uso se parece com:

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

o que permitiria muitos relacionamentos entre resulte other.

Novamente com valores unificados, podemos fazer o seguinte:

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

aqui, chamamos o retorno de chamada com um intervalo de resultados. Podemos até fazer isso repetidamente.

Usando isso, você pode ter uma função que passa eficientemente megabytes de dados sem fazer nenhuma alocação fora da pilha.

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
}

Agora, std::functioné um pouco pesado para isso, pois estaríamos fazendo isso em ambientes sem alocação sem sobrecarga. Então, queremos um function_viewque nunca aloque.

Outra solução é:

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

onde, em vez de pegar o retorno de chamada e invocá-lo, foo retorna uma função que aceita o retorno de chamada.

foo (7) ([&] (resultado int, resultado int outro) {/ * código * /}); isso quebra os parâmetros de saída dos parâmetros de entrada por colchetes separados.

Com variant ecorotinas, você pode criar fooum gerador de uma variante dos tipos de retorno (ou apenas do tipo de retorno). A sintaxe ainda não foi corrigida, portanto não darei exemplos.

No mundo dos sinais e slots, uma função que expõe um conjunto de sinais:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

permite que você crie um fooque funcione de forma assíncrona e transmita o resultado quando terminar.

Abaixo dessa linha, temos uma variedade de técnicas de pipeline, nas quais uma função não faz algo, mas organiza a conexão de dados de alguma forma, e a ação é relativamente independente.

foo( int_source )( int_dest1, int_dest2 );

então esse código não faz nada até int_sourceter números inteiros para fornecê-lo. Quando isso acontecer, int_dest1e int_dest2comece a receber os resultados.


Esta resposta contém mais informações do que outras respostas! em particular, informações sobre auto&&[result, other_result]=foo();funções retornando tuplas e estruturas. Obrigado!
jjmontes

Aprecio esta resposta exaustiva, especialmente porque ainda estou preso ao C ++ 11 e, portanto, não posso usar algumas das soluções mais modernas que outras pessoas propõem.
GuyGizmo

5

Use uma estrutura ou uma classe para o valor de retorno. O uso std::pairpode funcionar por enquanto, mas

  1. é inflexível se você decidir mais tarde que deseja obter mais informações;
  2. não está muito claro na declaração da função no cabeçalho o que está sendo retornado e em que ordem.

Retornar uma estrutura com nomes de variáveis ​​de membros com documentação automática provavelmente será menos suscetível a erros para qualquer pessoa que use sua função. Vestindo meu chapéu de colega de trabalho por um momento, sua divide_resultestrutura é fácil para mim, um potencial usuário de sua função, de entender imediatamente após 2 segundos. Brincar com parâmetros de saída ou pares e tuplas misteriosos levaria mais tempo para ler e pode ser usado incorretamente. E provavelmente mesmo depois de usar a função algumas vezes, ainda não me lembrarei da ordem correta dos argumentos.


4

Se sua função retornar um valor por referência, o compilador não poderá armazená-lo em um registro ao chamar outras funções, porque, teoricamente, a primeira função pode salvar o endereço da variável passada a ele em uma variável acessível globalmente, e quaisquer funções chamadas subseqüentemente podem altere-o para que o compilador (1) salve o valor dos registradores de volta à memória antes de chamar outras funções e (2) releia-o quando necessário da memória novamente após qualquer uma dessas chamadas.

Se você retornar por referência, a otimização do seu programa sofrerá


4

Aqui, estou escrevendo um programa que está retornando vários valores (mais de dois valores) em c ++. Este programa é executável em c ++ 14 (G ++ 4.9.2). programa é como uma 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;
}

Portanto, você pode entender claramente que dessa maneira você pode retornar vários valores de uma função. usando std :: pair, somente 2 valores podem ser retornados, enquanto std :: tuple pode retornar mais de dois valores.


4
Com o C ++ 14, você também pode usar o autotipo de retorno calpara tornar isso ainda mais limpo. (OMI).
Sfjac

3

Costumo usar valores fora de funções em funções como essa, porque me apego ao paradigma de uma função retornando códigos de sucesso / erro e gosto de manter as coisas uniformes.


2

As alternativas incluem matrizes, geradores e inversão de controle , mas nenhuma é apropriada aqui.

Alguns (por exemplo, a Microsoft no histórico do Win32) tendem a usar parâmetros de referência para simplificar, porque é claro quem aloca e como ficará na pilha, reduz a proliferação de estruturas e permite um valor de retorno separado para o sucesso.

Programadores "puros" preferem a estrutura, assumindo que é o valor da função (como é o caso aqui), em vez de algo que é tocado incidentalmente pela função. Se você tivesse um procedimento mais complicado ou algo com state, provavelmente usaria referências (supondo que tenha um motivo para não usar uma classe).


2

Eu diria que não há método preferido, tudo depende do que você fará com a resposta. Se os resultados forem usados ​​juntos em processamento adicional, as estruturas farão sentido; caso contrário, eu tenderia a passar como referências individuais, a menos que a função fosse usada em uma instrução composta:

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

Costumo escolher distribuir 'estruturas' por referência na lista de parâmetros, em vez de passar a cópia por sobrecarga de retornar uma nova estrutura (mas isso está suando as pequenas coisas).

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

Os parâmetros estão confusos? Um parâmetro enviado como referência sugere que o valor será alterado (em oposição a uma referência const). A nomeação sensível também remove a confusão.


1
Eu acho que é um pouco confuso. Alguém que lê o código que o chama vê "divide (a, b, c);". Não há indicação de que c seja um valor externo até que eles procurem a assinatura. Mas esse é um medo geral de parâmetros de referência não-const, e não de particular para esta questão.
Steve Jessop

2

Por que você insiste em uma função com vários valores de retorno? Com o OOP, você pode usar uma classe que oferece uma função regular com um único valor de retorno e qualquer número de "valores de retorno" adicionais, como abaixo. A vantagem é que o chamador pode escolher olhar para os membros de dados extras, mas não é necessário fazer isso. Esse é o método preferido para chamadas complicadas de banco de dados ou rede, onde muitas informações adicionais de retorno podem ser necessárias, caso ocorram erros.

Para responder à sua pergunta original, este exemplo possui um método para retornar o quociente, que é o que a maioria dos chamadores pode precisar e, além disso, após a chamada do método, você pode obter o restante como membro de dados.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};

1
Eu acho que há casos em que isso é ineficiente. Por exemplo, você tem um loop for único que gera vários valores de retorno. Se você dividir esses valores em funções separadas, precisará executar o loop uma vez para cada valor.
jiggunjer

1
@jiggunjer Você pode executar o loop uma vez e armazenar os vários valores de retorno em membros de dados de classe separados. Isso ressalta a flexibilidade do conceito de POO.
Roland

2

em vez de retornar vários valores, basta retornar um deles e fazer referência a outros na função necessária, por exemplo:

int divide(int a,int b,int quo,int &rem)

Eu não mencionei isso na própria pergunta? Além disso, veja minhas objeções na minha resposta .
Fred Larson

1

A tupla de impulso seria minha escolha preferida para um sistema generalizado de retornar mais de um valor de uma função.

Exemplo possível:

include "boost/tuple/tuple.hpp"

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

{
  return make_tuple(dividend / divisor,dividend % divisor )
}

1

Podemos declarar a função de modo que ela retorne uma variável definida pelo usuário do tipo de estrutura ou um ponteiro para ela. E pela propriedade de uma estrutura, sabemos que uma estrutura em C pode conter vários valores de tipos assimétricos (ou seja, uma variável int, quatro variáveis ​​char, duas variáveis ​​float e assim por diante ...)


1

Gostaria apenas de fazer referência por apenas alguns valores de retorno, mas para tipos mais complexos, você também pode fazer o seguinte:

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 "estático" para limitar o escopo do tipo de retorno a esta unidade de compilação, se for apenas um tipo de retorno temporário.

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

Definitivamente, essa não é a maneira mais bonita de fazer isso, mas funcionará.


-2

Aqui está um exemplo completo desse tipo de solução de problema

#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;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.