c++ - ¿Por qué los objetos de función deben ser pasados por valor?
pass-by-value function-object (3)
Desde Effective STL (ya que parece que te gusta Scott Meyers), elemento 38 Design funtor classes para pass-by-value .
"Tanto en C como en C ++, los punteros de función se pasan por valor. Los objetos de función STL se modelan después de los punteros de función, por lo que la convención en el STL es que los objetos de función también se pasan por valor cuando se pasan a las funciones".
Esto tiene algunos beneficios y algunas implicaciones, como dijo @Jerry Coffin, el compilador puede hacer algunas optimizaciones como alinear el código para evitar llamadas de función (tiene que marcar su functor como inline). Un buen ejemplo de este caso es la comparación de rendimiento qsort vs std :: sort, donde std :: sort usa los funtores en línea superando a qsort por mucho, puede encontrar más información al respecto en Effective STL donde se discute ampliamente y se menciona en varias capítulos
Esto también tiene varias implicaciones, ya que los objetos de función se pasan y se devuelven por valor, debe asegurarse de que su objeto tenga mecanismos de copia bien definidos, sean de tamaño pequeño (de lo contrario, podrían ser costosos) y son monomórficos (ya que pasan polimórficos). los objetos por valor pueden dar como resultado el corte del objeto).
Acabo de leer el libro clásico "Effective C ++, 3rd Edition", y en el ítem 20 el autor concluye que los tipos incorporados, los iteradores STL y los tipos de objetos de función son más apropiados para el paso por valor . Podría entender bien la razón de los tipos de iteradores e incorporados, pero ¿por qué el objeto de función debe ser pasado por valor , como sabemos que es de tipo clase?
En un caso típico, un objeto de función tendrá poco o (más a menudo) ningún estado persistente. En tal caso, pasar por valor no puede requerir que se pase realmente nada, el "valor" que se pasa es básicamente poco o nada más que un marcador de posición para "este es el objeto".
Dada la pequeña cantidad de código en muchos objetos de función, esto conduce a una optimización adicional: a menudo es bastante fácil para el compilador expandir el código para el objeto de función en línea, por lo que no se pasa ningún parámetro y no se involucra ninguna llamada de función.
Un compilador puede ser capaz de hacer lo mismo cuando pasa un puntero o una referencia, pero no es tan fácil: es mucho más común que termine con la creación de un objeto, su dirección y la función. llamar al operador para ese objeto que se invoca a través de ese puntero.
Edición: es probable que también valga la pena mencionar que lo mismo se aplica a las lambdas, ya que en realidad no son más que objetos de disfraz. No sabe el nombre de la clase, pero crean una clase en el ámbito inmediato que sobrecarga al operador de llamada a la función, que es lo que se invoca cuando "llama" a la lambda. [Gracias @Mark Garcia.]
La razón # 1 para pasar objetos de función por valor es que la biblioteca estándar requiere que los objetos de función que pase a sus algoritmos sean copiables. C ++ 11 §25.1 / 10:
[Nota: a menos que se especifique lo contrario, los algoritmos que toman objetos de función como argumentos tienen permitido copiar esos objetos de función libremente. Los programadores para quienes la identidad del objeto es importante deben considerar el uso de una clase de envoltorio que apunte a un objeto de implementación no copiado como
reference_wrapper<T>
(20.8.3), o alguna solución equivalente. "Nota final"
Las otras respuestas hacen un gran trabajo al explicar el razonamiento.