Pergunta

no Signals impulso biblioteca, eles são sobrecarregar o operador ().

Trata-se de uma convenção em C ++? Para chamadas de retorno, etc.?

Eu vi isso no código de um colega de trabalho (que passa a ser um grande fã Boost). De toda a bondade impulso lá fora, isso só levou a confusão para mim.

Qualquer visão sobre a razão para essa sobrecarga?

Foi útil?

Solução

Um dos objetivo principal ao sobrecarregar operator () é criar um functor. Um functor age como uma função, mas tem as vantagens de que é stateful, o que significa que pode manter os dados refletindo seu estado entre as chamadas.

Aqui está um exemplo functor simples:

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"

Functores são muito utilizadas com programação genérica. Muitos algoritmos STL está escrito de uma forma muito geral, de modo que você pode plug-in sua própria função / functor no algoritmo. Por exemplo, o algoritmo de std :: for_each permite que você aplique uma operação em cada elemento de uma gama. Pode ser implementado algo assim:

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

Você vê que este algoritmo é muito genérico, uma vez que é parametrizada por uma função. Usando o operador (), esta função permite usar um functor ou um ponteiro de função. Aqui está um exemplo mostrando ambas as possibilidades:

void print(int i) { std::cout << i << std::endl; }
...    
std::vector<int> 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

No que diz respeito à sua pergunta sobre operator () sobrecarga, bem sim, é possível. Você pode perfeitamente escrever uma functor que tem operador vários parênteses, contanto que você respeitar as regras básicas do método de sobrecarga (por exemplo sobrecarga só do tipo de retorno não é possível).

Outras dicas

Ele permite que uma classe agir como uma função. Eu usei-o em uma classe de log onde a chamada deve ser uma função, mas eu queria o benefício extra da classe.

para algo como isto:

logger.log("Log this message");

transforma em isto:

logger("Log this message");

Muitos responderam que faz um functor, sem dizer uma grande razão pela qual um functor é melhor do que uma função velho liso.

A resposta é que um functor pode ter estado. Considere uma função soma -. Ele precisa manter uma execução total

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

Um functor não é uma função, então você não pode sobrecarregá-lo.
Seu colega de trabalho está correto no entanto, que a sobrecarga de operador () é utilizada para criar "functors" - objetos que podem ser chamados como funções. Em combinação com modelos de espera "na forma de função" argumentos isso pode ser bastante poderoso, porque a distinção entre um objeto e uma função torna-se turva.

Como outros cartazes disse: functors têm uma vantagem sobre funções simples em que eles podem ter estado. Este estado pode ser utilizado através de uma única iteração (por exemplo, para calcular a soma de todos os elementos em um recipiente) ou ao longo de várias iterações (por exemplo, para encontrar todos os elementos em vários recipientes que satisfaçam os critérios particulares).

Comece a usar std::for_each, std::find_if, etc. mais frequentemente em seu código e você verá por que é útil para ter a capacidade de sobrecarregar o operador (). Ele também permite que functors e tarefas para ter um método chamado claro que não irá conflito com os nomes de outros métodos nas classes derivadas.

Você também pode olhar por cima C ++ FAQ Matrix exemplo. Há bons usos para fazê-lo, mas é claro que depende do que você está tentando realizar.

Functores são basicamente como ponteiros de função. Eles são geralmente destinados a ser copiável (como ponteiros de função) e invocado na mesma maneira como os ponteiros de função. O principal benefício é que quando você tem um algoritmo que funciona com um functor templated, a chamada de função para o operador () pode ser embutido. No entanto, ponteiros de função são ainda functors válidos.

O uso de operador () para formar functors em C ++ está relacionada com paradigmas de programação funcional que normalmente fazem uso de um conceito similar: fechamentos .

Uma força que eu posso ver, no entanto, isso pode ser discutido, é que a assinatura do operador () parece e se comporta o mesmo em diferentes tipos. Se tivéssemos um Reporter classe que tinha um relatório método membro (..), e, em seguida, um outro escritor classe, que tinha uma gravação método membro (..), teríamos de adaptadores de escrita se gostaríamos de usar ambas as classes como talvez um componente de modelo de algum outro sistema. Tudo o que importa é passar cordas ou o que você tem. Sem o uso de operador () sobrecarga ou escrever adaptadores do tipo especiais, você não poderia fazer coisas como

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

porque T tem uma exigência de que existe uma função de membro chamada gravação que aceita qualquer coisa implicitamente passível de conversão para const char * (ou melhor, const char []). A classe Reporter neste exemplo não tem que, assim que ter T (um parâmetro de modelo) sendo Reporter não seria suficiente para compilar.

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

T t;
t("Hello world");

no entanto, ele ainda exige explicitamente que o tipo T tem como um operador definido, então ainda temos um requisito em T. Pessoalmente, eu não acho que é muito estranho com functors como são comumente usados, mas eu preferiria ver outros mecanismos para esse comportamento. Em linguagens como C # você pode simplesmente passar em um delegado. Eu não sou muito familiarizado com ponteiros de função membro em C ++, mas eu poderia imaginar que você poderia conseguir o mesmo comportamento também há além.

Além de comportamento açúcar sintático Eu realmente não ver os pontos fortes de sobrecarga de operador para executar tais tarefas.

Estou certo de que há mais conscientemente as pessoas que têm razões melhores do que eu tenho, mas eu pensei que eu iria colocar para fora minha opinião para o resto de vocês para compartilhar.

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

my_functor();

É realmente:

my_functor.operator()();

Então, isso significa o seguinte:

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

Pode ser usado para sobrecarregar esta bem?

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

Outras mensagens ter feito um bom trabalho descrevendo como operador () funciona e por isso pode ser útil.

Eu recentemente vindo a utilizar algum código que faz muito uso extensivo de operador (). Uma desvantagem de uma sobrecarga deste operador é que alguns IDEs tornaram-se ferramentas menos eficaz como um resultado. Em Visual Studio, você pode geralmente clique com o botão direito em uma chamada de método para ir para a definição do método e / ou declaração. Infelizmente, VS não é suficiente inteligente para operador de índice () chamadas. Especialmente em código complexo com operador substituído definições () em todo o lugar, ele pode ser muito difícil descobrir qual pedaço de código está em execução onde. Em vários casos, eu descobri que eu tinha para executar o código e traço através dele para encontrar o que realmente estava em execução.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top