Função que Retorna Dois Valores

por Fabio A. Mazzarino

Há algum tempo me deparei com uma dúvida no Stack Overflow, um usuário perguntando se havia como fazer uma função que retornedois valores em C/C++. Tem como, e tem mais de uma forma de fazer.

Como exemplo vamos desenvolver funções que fazem conversão de coordenadas cartesianas (x, y) em coordenadas polares (r, a). A fórmula que faz essa conversão é simples:

r = sqrt(x * x + y * y);
a = atan2(y, x);

sqrt é uma função para raiz quadrada, e atan2 é uma função para calcular o arco-tangente através dos valores dos catetos.

C: Argumentos por Ponteiros

A forma mais simples de retornar dois valores é utilizar argumentos ponteiros:

#include <math.h>
#include <stdio.h>
void cart2polar(double x, double y, double *r, double *a) {
    if (r == NULL || a == NULL)
        return NULL;
    *r = sqrt(x * x + y * y);
    *a = atan2(y, x);
}
int main() {
    double r, a = 0.0;
    cart2polar(10, 10, &r, &a);
}

Neste caso é preciso tomar cuidado por conta dos parâmetros r e a serem ponteiros, é necessário verificar a validade dos pointeiros.

C: Retornar Estrutura de Dados

Neste caso vamos definir uma struct que encapsule os valores de retorno:

#include <math.h>
#include <stdio.h>
typedef struct {
    double r;
    double a;
} polar;
polar cart2polar(double x, double y) {
    polar p;
    p.r = sqrt(x * x + y * y);
    p.a = atan2(y, x);
    return p;
}
int main() {
    polar p = cart2polar(10, 10);
    printf("(10, 10) <=> %.2lf < %.2lf\n", p.r, p.a);
}

Apesar de ser necessário criar uma struct nova o código resultante é mais sucinto.

C++: Argumentos por Referência

Muito parecido com a solução de argumentos por ponteiros, porém sem a necessidade de gerenciar os ponteiros:

#include <cmath>
#include <iomanip>
#include <iostream>
void cart2polar(double x, double y, double &r, double &a) {
    r = sqrt(x * x + y * y);
    a = atan2(y, x);
}
int main() {
    double r, a;
    cart2polar(10, 10, r, a);
    std::cout << "(10, 10) <=> " <<
        std::fixed << std::setprecision(2) <<
        r << " < " << a << std::endl;
}

Utilizando argumentos por referência não há os riscos de se utilizar ponteiros, sem riscos de acesso por ponteiro nulo, nem de vazamento de memória.

C++: Retornando std::pair

Um pouco mais refinado, utilizar std::pair é muito parecido com criar uma struct para retornar os dois valores, com a vantagem de não ser necessário declarar um novo tipo, e com os campos anônimos.

#include <cmath>
#include <iomanip>
#include <iostream>
#include <utility>
std::pair<double, double> cart2polar(double x, double y) {
    double r = sqrt(x * x + y * y);
    double a = atan2(y, x);
    return std::pair<double, double>(r, a);
}
int main() {
    auto p = cart2polar(10, 10);
    std::cout << "(10, 10) <=> " <<
        std::fixed << std::setprecision(2) <<
        p.first << " < " << p.second << std::endl;
}

Note a utilização do std::pair, através dos atributos first e second.

C++: Classe

Uma última opção é a criação de uma classe, e neste caso um amplo leque de possibilidades. Vamos ver uma possibilidade.

#include <cmath>

class polar {
private:
    double r;
    double a;
public:
    polar() : r(0.0), a(0.0) {}
    polar(double _r, double _a) : r(_r), a(_a) { }
    
    void set(double _r, double _a) {
        r = _r;
        a = _a;
    }
    
    void setByRect(double x, double y) {
        r = sqrt(x * x + y * y);
        a = atan2(y, x);
    }
    
    double getr() {
        return r;
    }
    double geta() {
        return a;
    }
  
};

Mas neste caso as possibilidades são incontáveis, e a melhor solução sempre estará ligada com a forma de utilização da conversão específica.