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.
std::tuple
.std::tie
stackoverflow.com/a/2573822/502144No C ++ 11, você pode:
No C ++ 17:
ou com estruturas:
fonte
struct
linha para fora do corpo da função e substituir oauto
retorno da função porresult
.divide
em um arquivo cpp separado? Eu recebo o erroerror: use of ‘auto divide(int, int)’ before deduction of ‘auto’
. Como eu resolvo isso?Pessoalmente, geralmente não gosto de parâmetros de retorno por vários motivos:
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:
Isso imprime velocidade e percurso no solo, ou percurso e velocidade no solo? Não é óbvio.
Compare com isso:
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.
fonte
struct
likeVelocity
é boa. No entanto, uma preocupação é que polua o espaço para nome. Suponho que, com C ++ 11, ostruct
pode ter um nome de tipo longo e um possa ser usadoauto result = calculateResultingVelocity(...)
.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 comostruct my_func_result_t
...auto
, portantoauto result = my_func();
é trivialmente obtido.std :: pair é essencialmente sua solução struct, mas já definida para você e pronta para se adaptar a dois tipos de dados.
fonte
É totalmente dependente da função real e do significado dos vários valores e seus tamanhos:
fonte
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 ..
fonte
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.
fonte
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 .
A coisa bonita sobre isso é que é garantido que não causa qualquer copiar ou mover. Você também pode tornar o exemplo
many
struct variadic. Mais detalhes:Retornando agregados variadicos (struct) e sintaxe para o modelo variadico C ++ 17 'guia de dedução de construção'
fonte
Existem várias maneiras de retornar vários parâmetros. Eu vou ser exhastive.
Use parâmetros de referência:
use os parâmetros do ponteiro:
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:
então nós podemos fazer:
e tudo está bem.
foo
nã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:
whick funciona bem em todas as versões do C ++ e no c ++ 17 isso também permite:
a custo zero. Os parâmetros nem sequer podem ser movidos graças à garantia garantida.
Poderíamos retornar um
std::tuple
:que tem a desvantagem de que os parâmetros não são nomeados. Isso permite que oc ++ 17:
também. Antes dec ++ 17 em vez disso, podemos fazer:
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:e agora os chamadores fazem:
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:
o
value
retorno 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.
cujo uso se parece com:
o que permitiria muitos relacionamentos entre
result
eother
.Novamente com valores unificados, podemos fazer o seguinte:
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.
Agora,
std::function
é um pouco pesado para isso, pois estaríamos fazendo isso em ambientes sem alocação sem sobrecarga. Então, queremos umfunction_view
que nunca aloque.Outra solução é:
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
ec ++ 20corotinas, você pode criarfoo
um 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:
permite que você crie um
foo
que 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.
então esse código não faz nada até
int_source
ter números inteiros para fornecê-lo. Quando isso acontecer,int_dest1
eint_dest2
comece a receber os resultados.fonte
auto&&[result, other_result]=foo();
funções retornando tuplas e estruturas. Obrigado!Use uma estrutura ou uma classe para o valor de retorno. O uso
std::pair
pode funcionar por enquanto, masRetornar 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_result
estrutura é 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.fonte
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á
fonte
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.
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.
fonte
auto
tipo de retornocal
para tornar isso ainda mais limpo. (OMI).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.
fonte
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).
fonte
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.
fonte
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.
fonte
em vez de retornar vários valores, basta retornar um deles e fazer referência a outros na função necessária, por exemplo:
fonte
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:
fonte
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 ...)
fonte
Gostaria apenas de fazer referência por apenas alguns valores de retorno, mas para tipos mais complexos, você também pode fazer o seguinte:
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.
Definitivamente, essa não é a maneira mais bonita de fazer isso, mas funcionará.
fonte
Aqui está um exemplo completo desse tipo de solução de problema
fonte