Eu tenho uma classe chamada Writer
que tem uma função writeVector
assim:
void Drawer::writeVector(vector<T> vec, bool index=true)
{
for (unsigned int i = 0; i < vec.size(); i++) {
if (index) {
cout << i << "\t";
}
cout << vec[i] << "\n";
}
}
Estou tentando não ter um código duplicado, mas ainda me preocupo com o desempenho. Na função, estou fazendo a if (index)
verificação em cada rodada do meu for
-loop, embora o resultado seja sempre o mesmo. Isso é contra "se preocupar com o desempenho".
Eu poderia facilmente evitar isso colocando o cheque fora do meu for
-loop. No entanto, vou obter muitos códigos duplicados:
void Drawer::writeVector(...)
{
if (index) {
for (...) {
cout << i << "\t" << vec[i] << "\n";
}
}
else {
for (...) {
cout << vec[i] << "\n";
}
}
}
Portanto, essas duas soluções são "ruins" para mim. O que estive pensando são duas funções privadas, uma delas sai do índice e depois chama a outra. O outro apenas supera o valor. No entanto, não consigo descobrir como usá-lo com meu programa, ainda preciso if
verificar para ver qual chamar ...
De acordo com o problema, o polimorfismo parece a solução correta. Mas não consigo ver como devo usá-lo aqui. Qual seria a forma preferida de resolver esse tipo de problema?
Este não é um programa real, estou apenas interessado em aprender como esse tipo de problema deve ser resolvido.
fonte
Respostas:
Passe o corpo do loop como um functor. Ele fica embutido em tempo de compilação, sem penalidade de desempenho.
A ideia de passar o que varia é onipresente na Biblioteca Padrão C ++. É o chamado padrão de estratégia.
Se você tem permissão para usar C ++ 11, pode fazer algo assim:
Este código não é perfeito, mas essa é a ideia.
No antigo C ++ 98, era assim:
Novamente, o código está longe de ser perfeito, mas dá uma ideia.
fonte
for(int i=0;i<100;i++){cout<<"Thank you!"<<endl;}
: D Este é o tipo de solução que eu estava procurando, funciona perfeitamente :) Você poderia melhorá-la com alguns comentários (tive problemas para entendê-la no início), mas eu consegui sem problemas :)cout << e << "\n";
), ainda haveria bastante duplicação de código.Se este for realmente o caso, o preditor de ramificação não terá problemas em prever o resultado (constante). Como tal, isso só causará uma pequena sobrecarga para previsões erradas nas primeiras iterações. Não há nada com que se preocupar em termos de desempenho
Nesse caso, eu defendo manter o teste dentro do loop para maior clareza.
fonte
std::cout
)Para expandir a resposta de Ali, que é perfeitamente correta, mas ainda assim duplica algum código (parte do corpo do loop, infelizmente isso é dificilmente evitável ao usar o padrão de estratégia) ...
Concedido, neste caso específico, a duplicação de código não é muito, mas há uma maneira de reduzi-la ainda mais, o que é útil se o corpo da função for maior do que apenas algumas instruções .
A chave é usar a capacidade do compilador de realizar dobragem constante / eliminação de código morto . Podemos fazer isso mapeando manualmente o valor de tempo de execução de
index
para um valor de tempo de compilação (fácil de fazer quando há apenas um número limitado de casos - dois neste caso) e usar um argumento de modelo não-tipo que é conhecido na compilação -Tempo:Desta forma, terminamos com o código compilado que é equivalente ao seu segundo exemplo de código (externo
if
/ internofor
), mas sem duplicar o código. Agora podemos tornar a versão do modelowriteVector
tão complicada quanto quisermos, sempre haverá um único pedaço de código para manter.Observe como a versão do modelo (que usa uma constante de tempo de compilação na forma de um argumento de modelo não-tipo) e a versão não-modelo (que usa uma variável de tempo de execução como um argumento de função) estão sobrecarregadas. Isso permite que você escolha a versão mais relevante de acordo com suas necessidades, tendo uma sintaxe bastante semelhante e fácil de lembrar em ambos os casos:
fonte
doWriteVector
diretamente, mas concordo que o nome era lamentável. Acabei de alterá-lo para ter duaswriteVector
funções sobrecarregadas (um modelo, o outro uma função regular) para que o resultado seja mais homogêneo. Obrigado pela sugestão. ;)Na maioria dos casos, seu código já é bom para desempenho e legibilidade. Um bom compilador é capaz de detectar invariantes de loop e fazer otimizações apropriadas. Considere o exemplo a seguir, que é muito próximo ao seu código:
Estou usando a seguinte linha de comando para compilá-lo:
Então, vamos despejar a montagem:
O resultado da montagem
write_vector
é:Podemos ver que no início da função, verificamos o valor e saltamos para um dos dois loops possíveis:
Claro, isso só funciona se um compilador for capaz de detectar que uma condição é invariante real. Normalmente, funciona perfeitamente para sinalizadores e funções embutidas simples. Mas se a condição for "complexa", considere usar abordagens de outras respostas.
fonte