Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]

1. Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]

M.
XProtoman

(usa Fedora)

Enviado em 10/08/2016 - 22:40h

Boa noite a todos,

Conheço pouco sobre a utilização do operador (), existe algum uso comum dado pelos programadores C++?

É algo muito genérico que pode ser qualquer coisa?

Deve-se evitar utilizá-lo por poder ser algo genérico?

Obrigado.


  


2. MELHOR RESPOSTA

Perfil removido
removido

(usa Nenhuma)

Enviado em 10/08/2016 - 23:49h

Cara, encontrei isto:

http://web.itu.edu.tr/~bkurt/Courses/blg252e/blg252e_mod05.pdf

Olhe na página 217. É o que encontrei.

E tem este aqui também:

http://www.learncpp.com/cpp-tutorial/99-overloading-the-parenthesis-operator/

Comente sobre o que você entendeu, para debatermos.

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden


3. Re: Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 11/08/2016 - 09:01h

Para functors


#include <iostream>

struct Foo {
int operator()(int value) {
std::cout << "Viva o Linux" << '\n';
return value;
}
};

int main() {
Foo foo;
std::cout << foo(42) << '\n';
return 0;
}


A saída será:


Viva o Linux
42


Este artifício está em utilizar estrutura como função. É mais comum para o caso de callback.


#include <iostream>
#include <functional>

struct Foo {
void operator()()
{
std::cout << "Viva o Linux" << '\n';
}
};

struct Bar {
using Callback = std::function<void()>;

Bar(Callback callback) : m_callback(callback) {
callback();
}
Callback m_callback;
};

static void qux() {
std::cout << "Viva o C++" << '\n';
}

int main()
{
Foo foo;
Bar bar(foo);
bar.m_callback();
Bar baz(qux);
baz.m_callback();
return 0;
}


A saída será:


Viva o Linux
Viva o Linux
Viva o C++


Veja que o construtor de Bar necessita de uma função como parâmetro. Poderiamos simplemente passar um ponteiro de função, funcionaria igual, mas utilizando operator(), estamos adequando o uso de objeto de função.

Ou ainda, podemos abusar da programação genérica:


#include <iostream>

struct Foo {
template <typename T>
void operator()(T t) {
std::cout << "Viva o Linux: " << t << '\n';
}
};

template <typename T>
void qux(T t) {
t(42);
}

int main() {
Foo foo;
qux(foo);
return 0;
}


A saída será:


Viva o Linux: 42


Veja que em todos os casos, passamos um objeto e tratamos como uma função.

--
Uilian Ries
Linux Counter: 521986


4. Re: Sobrecarga do operador (), qual uso comum que é dado a ele?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 11/08/2016 - 09:02h

Sobrecarregar o operador de chamada de função é muito útil, e o mais legal é que, ao contrário dos demais operadores, que são todos unários ou binários, você pode passar quantos argumentos quiser e dos tipos que quiser.

Um uso muito comum é para criar classes de objetos com comportamento equivalente a funções. Um caso particular disso é para criar predicados (que são funções booleanas que retornam verdadeiro se uma determinada condição for verdadeira, a partir de testes sobre seus argumentos). Em C++, esses predicados costumam ser usados com algoritmos encontrados na biblioteca padrão, no cabeçalho <algorithm>.

Suponha que você tem uma coleção de números inteiros guardada em int_col, que é do tipo std::vector<int>, e que você queira copiar para uma segunda coleção int_col2 todos os números em int_col que sejam múltiplos de 3, usando um algoritmo de <algorithm> que invoca um predicado. Eis abaixo um jeito de fazer, indicando uma função, por meio de um ponteiro de função, como predicado.

#include <algorithm>
#include <vector>

bool is_mult_of_3(int n){ return !(n%3); } // Predicado.

void f(){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_3))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}


Eu usei uma função porque o predicado compara o argumento sempre com um valor fixo. Como eu usei std::find_if(), o predicado só pode receber um argumento. Mas se eu quisesse testar se o valor é múltiplo de um valor arbitrário, que eu pudesse definir a qualquer momento, mesmo durante a execução do programa, como eu poderia fazer?

Um jeito é criar uma classe com um construtor que receba as condições “fixas” do pedicado como argumento na hora de criar o objeto, e definir uma sobrecarga do operador () para esse objeto, de modo que ele possa ser usado como se fosse uma função (no nosso caso, com o papel de predicado). Veja como.

#Include <algorithm>
#include <vector>

class is_mult {
private:
int _n;
public:
is_multi(int n): _n(n) { }
bool operator()(int n){ return !(n%_n); }
};

void f(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
is_mult is_mult_of_n(n); // Por meio do construtor, defino como ai funcionar o predicado.
/* ... */
auto vi=int_col.cbegin();
// Agora, em vez de ponteiro de função, eu passo (uma referência a) o objeto.
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_n))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}


Se você já ouviu falar de lambdas no C++11, saiba que eles nada mais são do que objetos-função de classes anônimas, criadas automaticamente. O código da função g(), abaixo, produz o mesmo efeito da função f() mostrada acima, e o lambda gera uma classe anônima rigorosamente equivalente à classe is_mult.

void g(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;

/*
O “n” dentro dos colchetes cria um membro anônimo na
classe e um construtor que copia o valor de n para esse
membro anônimo. Quando usado no corpo do lambda, o
“n” é substituído não pelo valor que n tem dentro de g(),
mas sim pelo membro anônimo da classe correspondente
ao argumento n passado ao construtor.
*/
auto is_mult_of_n=[n](int m){ return !(m%n); };
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_n))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}


Ou ainda o seguinte, também rigorosamente equivalente, mas com o lambda expresso diretamente no invocação da função que vai usá-lo como argumento.

void g(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), [n](int m){ return !(m%n); }))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}


(Veja também o tópico sobre lambda que tivemos aqui há pouco tempo: https://www.vivaolinux.com.br/topico/C-C++/Funcao-Lambda-Em-C.)

Mas predicados, em particular, ou objetos-função, em geral, não esgotam o uso do operador ().

Sempre que, por uma questão de notação, o nome do objeto puder ser aplicado a um conjunto de argumentos diretamente, pode-se usar operador () para não ter de inventar um método com um nome redundante.

Um exemplo que eu já vi e já usei foi o de uma classe de polinômios. Na Matemática, quando você define um polinômio, o faz com algo numa forma parecida com isto “P(x)=2x²-4x+5”, e quando quer indicar o valor do polinômio no quando x vale, por exemplo, 1.2, você diz apenas “P(1.2)”. Em C++, com a sobrecarga de operator()()você pode conseguir uma notação muito próxima dessa notação matemática.

polynomial<double> P{2, -4, 5};  // P(x)=2x²-4x+5;
/* ... */
std::cout << "P(1.2)=" << P(1.2); // Calcula e imprime o polinômio P(x) quando x=1.2;


Sem a sobrecarga, talvez nós tivéssemos de inventar um método chamado evaluate_at(), e usar algo como P.evaluate_at(1.2), que além de fugir da notação matemática tradicional, também é mais cansativo de escrever e de ler.

Outro uso que eu já vi e já usei foi como método de acesso a elemento em classes que representam arrays multidimensionais, em que você não deseja dar ao usuário acesso a níveis separados do array. Compare os dois exemplos abaixo para entender.

// Array 3D, modo 1: array de array de array.
#include <array>

std::array<std::array<std::array<double, Z_size>, Y_size>, X_size> array3d;
// Poderia ser std::vector<std::vector<std::vector<double>>>, mas seria pior ainda!

void f(){
std::cout << array3d[x][y][z] << '\n'; // Acesso a um elemento. OK.
std::cout << array3d[x][y].data() << '\n'; // Acesso a um array inteiro. Isso é OK?
std::cout << array3d[x].data() << '\n'; // Acesso a um array de arrays. Isso é OK?
std::cout << array3d.data() << '\n'; // Acesso bruto a todos os dados. Isso é OK?
// Com vector em lugar de arrays, seria pior ainda, porque cada linha, coluna ou
// profundidade poderia ter números de elementos diferentes dos demais!!
}


// Modo 2: com uma classe que encapsula totalmente a representação do array 3D.
// Com o encapsulamento, perde-se a possibilidade de indexar os elementos
// com múltiplos níveis de colchetes, de modo que se usa o operador ().
template <unsigned X_sz, unsigned Y_sz, unsigned Z_sz, typename T>
class array3d_t {
private:
T [X_sz*Y_sz*Z_sz] _data;
public:
T &operator()(unsigned x, unsigned y, unsigned z){ return _data[(x*Y_sz+y)*Z_sz+z]; }
};

array3d_t<X_size, Y_size, Z_size, double> array3d;

void f(){
std::cout << array3d(x, y, z) << '\n'; // Acesso somente a cada elemento, nunca a subarrays.
}


Mas a sua imaginação é o limite. O fato de permitir qualquer quantidade de elementos de quaisquer tipos dá a você inúmeras possibilidades.

Eu só recomendo cautela para que a notação faça sentido. Espera-se que a sobrecarga operador () -- como, aliás, a de qualquer operador -- ajude o código a ser naturalmente mais inteligível, não que o torne mais obscuro.


5. Re: Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]

M.
XProtoman

(usa Fedora)

Enviado em 14/08/2016 - 22:46h

Obrigado listeiro_037, uilianries e paulo1205.
____________________
“Mas nós sabemos que a razão de a maioria estar aqui, é a nossa afinidade com a desobediência!” (Morpheus)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts