Tengo una clase llamada Writer
que tiene una función writeVector
como esta:
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";
}
}
Estoy tratando de no tener un código duplicado, sin dejar de preocuparme por el rendimiento. En la función, estoy haciendo la if (index)
verificación en cada ronda de mi for
bucle, aunque el resultado es siempre el mismo. Esto va en contra de "preocuparse por el rendimiento".
Podría evitarlo fácilmente colocando el cheque fuera de mi for
bucle. Sin embargo, obtendré un montón de código duplicado:
void Drawer::writeVector(...)
{
if (index) {
for (...) {
cout << i << "\t" << vec[i] << "\n";
}
}
else {
for (...) {
cout << vec[i] << "\n";
}
}
}
Así que estas son dos soluciones "malas" para mí. Lo que he estado pensando son dos funciones privadas, una de ellas sale del índice y luego llama a la otra. El otro solo supera el valor. Sin embargo, no puedo averiguar cómo usarlo con mi programa, todavía necesitaría el if
cheque para ver a cuál llamar ...
Según el problema, el polimorfismo parece una solución correcta. Pero no veo cómo debería usarlo aquí. ¿Cuál sería la forma preferida de resolver este tipo de problema?
Este no es un programa real, solo estoy interesado en aprender cómo se debe resolver este tipo de problema.
fuente
Respuestas:
Pase en el cuerpo del bucle como un funtor. Se inserta en tiempo de compilación, sin penalización de rendimiento.
La idea de pasar lo que varía es omnipresente en la biblioteca estándar de C ++. Se llama patrón de estrategia.
Si se le permite usar C ++ 11, puede hacer algo como esto:
Este código no es perfecto pero entiendes la idea.
En el antiguo C ++ 98 se ve así:
Una vez más, el código está lejos de ser perfecto, pero te da la idea.
fuente
for(int i=0;i<100;i++){cout<<"Thank you!"<<endl;}
: D Este es el tipo de solución que estaba buscando, funciona como un encanto :) Podrías mejorarlo con pocos comentarios (tuve problemas para entenderlo al principio), pero lo obtuve, así que no hay problema :)cout << e << "\n";
) todavía habría bastante duplicación de código.Si este es realmente el caso, el predictor de rama no tendrá problemas para predecir el resultado (constante). Como tal, esto solo causará una leve sobrecarga por errores de predicción en las primeras iteraciones. No hay nada de qué preocuparse en términos de rendimiento.
En este caso, abogo por mantener la prueba dentro del ciclo para mayor claridad.
fuente
std::cout
)Para ampliar la respuesta de Ali, que es perfectamente correcta pero aún duplica algo de código (parte del cuerpo del bucle, desafortunadamente, esto es difícilmente evitable cuando se usa el patrón de estrategia) ...
Concedido en este caso particular, la duplicación de código no es mucha, pero hay una manera de reducirla aún más, lo cual es útil si el cuerpo de la función es más grande que unas pocas instrucciones .
La clave es utilizar la capacidad del compilador para realizar una eliminación constante de plegado / código muerto . Podemos hacer eso asignando manualmente el valor de tiempo de ejecución de
index
a un valor de tiempo de compilación (fácil de hacer cuando hay solo un número limitado de casos, dos en este caso) y usar un argumento de plantilla que no sea de tipo que se conoce en la compilación -hora:De esta manera terminamos con un código compilado que es equivalente a su segundo ejemplo de código (externo
if
/ internofor
) pero sin duplicar el código nosotros mismos. Ahora podemos hacer que la versión de la plantilla seawriteVector
tan complicada como queramos, siempre habrá una sola pieza de código para mantener.Observe cómo la versión de la plantilla (que toma una constante de tiempo de compilación en forma de un argumento de plantilla sin tipo) y la versión sin plantilla (que toma una variable de tiempo de ejecución como argumento de función) están sobrecargadas. Esto le permite elegir la versión más relevante en función de sus necesidades, teniendo una sintaxis bastante similar y fácil de recordar en ambos casos:
fuente
doWriteVector
directamente, pero estoy de acuerdo en que el nombre era desafortunado. Lo acabo de cambiar para que tenga doswriteVector
funciones sobrecargadas (una plantilla, la otra una función regular) para que el resultado sea más homogéneo. Gracias por la sugerencia. ;)En la mayoría de los casos, su código ya es bueno para el rendimiento y la legibilidad. Un buen compilador es capaz de detectar invariantes de bucle y realizar las optimizaciones adecuadas. Considere el siguiente ejemplo que está muy cerca de su código:
Estoy usando la siguiente línea de comando para compilarlo:
Entonces, descarguemos el ensamblaje:
El resultado ensamblado de
write_vector
es:Podemos ver que al inicio de la función, verificamos el valor y saltamos a uno de los dos bucles posibles:
Por supuesto, esto solo funciona si un compilador es capaz de detectar que una condición es invariante real. Por lo general, funciona perfectamente para banderas y funciones en línea simples. Pero si la condición es "compleja", considere usar enfoques de otras respuestas.
fuente