Por que replace o operador ()?

Na biblioteca Boost Signals , eles estão sobrecarregando o operador ().

Isso é uma convenção em C ++? Para retornos de chamada, etc.

Eu vi isso no código de um colega de trabalho (que por acaso é um grande fã do Boost). De toda a bondade do Boost, isso só me confundiu.

Alguma idéia da razão dessa sobrecarga?

Um dos principais objectives quando sobrecarregar operator () é criar um functor. Um functor age como uma function, mas tem as vantagens de ser stateful, ou seja, pode manter os dados refletindo seu estado entre as chamadas.

Aqui está um exemplo simples de functor:

struct Accumulator { int counter = 0; int operator()(int i) { return counter += i; } } ... Accumulator acc; cout << acc(10) << endl; //prints "10" cout << acc(20) << endl; //prints "30" 

Os funcionais são muito usados ​​com programação genérica. Muitos algoritmos de STL são escritos de uma forma muito geral, de modo que você pode inserir sua própria function / functor no algoritmo. Por exemplo, o algoritmo std :: for_each permite aplicar uma operação em cada elemento de um intervalo. Poderia ser implementado algo assim:

 template  void for_each(InputIterator first, InputIterator last, Functor f) { while (first != last) f(*first++); } 

Você vê que esse algoritmo é muito genérico, pois é parametrizado por uma function. Usando o operador (), essa function permite usar um functor ou um ponteiro de function. Aqui está um exemplo mostrando as duas possibilidades:

 void print(int i) { std::cout << i << std::endl; } ... std::vector vec; // Fill vec // Using a functor Accumulator acc; std::for_each(vec.begin(), vec.end(), acc); // acc.counter contains the sum of all elements of the vector // Using a function pointer std::for_each(vec.begin(), vec.end(), print); // prints all elements 

Quanto à sua pergunta sobre sobrecarga operator (), bem, sim, é possível. Você pode perfeitamente escrever um functor que tenha vários operadores de parênteses, desde que você respeite as regras básicas de sobrecarga de methods (por exemplo, sobrecarregar apenas o tipo de retorno não é possível).

Permite que uma class aja como uma function. Eu usei isso em uma class de registro onde a chamada deveria ser uma function, mas eu queria o benefício extra da class.

então algo assim:

 logger.log("Log this message"); 

se transforma nisso:

 logger("Log this message"); 

Muitos responderam que faz um functor, sem dizer uma grande razão pela qual um functor é melhor que uma function antiga.

A resposta é que um functor pode ter estado. Considere uma function de sum – ela precisa manter um total em execução.

 class Sum { public: Sum() : m_total(0) { } void operator()(int value) { m_total += value; } int m_total; }; 

Um functor não é uma function, portanto você não pode sobrecarregá-lo.
Seu colega de trabalho está correto embora a sobrecarga de operator () seja usada para criar “functors” – objects que podem ser chamados de funções. Em combinação com modelos que esperam argumentos “semelhantes a funções”, isso pode ser bastante poderoso, porque a distinção entre um object e uma function fica desfocada.

Como outros pôsteres disseram: os functores têm uma vantagem sobre as funções simples, pois podem ter estado. Esse estado pode ser usado em uma única iteração (por exemplo, para calcular a sum de todos os elementos em um contêiner) ou em várias iterações (por exemplo, para localizar todos os elementos em vários contêineres que satisfazem critérios específicos).

Comece a usar std::for_each , std::find_if , etc. com mais freqüência em seu código e você verá porque é útil ter a capacidade de sobrecarregar o operador (). Também permite que functores e tarefas tenham um método de chamada claro que não conflite com os nomes de outros methods nas classs derivadas.

Você também pode examinar o exemplo Matrix do C ++ faq . Há bons usos para fazer isso, mas é claro que depende do que você está tentando realizar.

Functors são basicamente como pointers de function. Eles geralmente são planejados para serem copiáveis ​​(como os pointers de function) e chamados da mesma maneira que os pointers de function. O principal benefício é que, quando você tem um algoritmo que funciona com um functor modelado, a chamada de function para operator () pode ser embutida. No entanto, os pointers de function ainda são functores válidos.

O uso de operator () para formar functores em C ++ está relacionado a paradigmas de functional programming que usualmente utilizam um conceito similar: closures .

Uma força que posso ver, no entanto, isso pode ser discutido, é que a assinatura de operator () se parece e se comporta da mesma forma em diferentes tipos. Se tivéssemos um Reporter de class que tivesse um relatório de método de membro (..) e outra class Writer, que tivesse um método de membro write (..), teríamos que escrever adaptadores se quisermos usar ambas as classs como talvez um componente de modelo de algum outro sistema. Tudo o que importa é passar as cordas ou o que você tem. Sem o uso de operator () sobrecarregando ou escrevendo adaptadores de tipo especiais, você não poderia fazer coisas como

 T t; t.write("Hello world"); 

porque T tem um requisito de que existe uma function de membro chamada write que aceita qualquer coisa implicitamente atribuível a const char * (ou melhor, const char []). A class Reporter neste exemplo não tem isso, então ter o T (um parâmetro de modelo) sendo Reporter não seria compilado.

No entanto, até onde eu posso ver isso funcionaria com diferentes tipos

 T t; t("Hello world"); 

no entanto, ele ainda exige explicitamente que o tipo T tenha tal operador definido, então ainda temos um requisito em T. Pessoalmente, eu não acho que é muito estranho com functores como eles são comumente usados, mas eu prefiro ver outros mecanismos para esse comportamento. Em linguagens como o C #, você poderia simplesmente passar um delegado. Eu não estou muito familiarizado com pointers de function de membro em C ++, mas eu poderia imaginar que você poderia conseguir o mesmo comportamento lá também.

Diferente do comportamento de açúcar sintático, eu realmente não vejo os pontos fortes da sobrecarga do operador para executar tais tarefas.

Tenho certeza de que há mais pessoas conscientemente que têm melhores razões do que eu, mas achei que daria minha opinião para o resto de vocês compartilharem.

Outro colega de trabalho apontou que poderia ser uma maneira de disfarçar objects functor como funções. Por exemplo, isso:

 my_functor(); 

É realmente:

 my_functor.operator()(); 

Então isso significa isso:

 my_functor(int n, float f){ ... }; 

Pode ser usado para sobrecarregar isso também?

 my_functor.operator()(int n, float f){ ... }; 

Outros posts fizeram um bom trabalho descrevendo como o operador () funciona e por que ele pode ser útil.

Eu recentemente tenho usado algum código que faz uso muito extensivo de operator (). Uma desvantagem de sobrecarregar este operador é que alguns IDEs tornam-se ferramentas menos eficazes como resultado. No Visual Studio, normalmente você pode clicar com o botão direito do mouse em uma chamada de método para ir para a definição do método e / ou declaração. Infelizmente, o VS não é inteligente o suficiente para indexar as chamadas operator (). Especialmente em código complexo com definições overridden de operator () em todo o lugar, pode ser muito difícil descobrir qual parte do código está sendo executada onde. Em vários casos, descobri que precisava executar o código e rastreá-lo para encontrar o que realmente estava sendo executado.