El último parámetro de la plantilla for_each
es un functor. Functor es algo que se puede "llamar" utilizando el operador ()
(posiblemente con argumentos). Por Defintion, hay dos tipos distintivos de funtores:
- funciones que no son miembros ordinarios son funtores.
- Los objetos del tipo de clase con el operador sobrecargado
()
(llamados objetos de función) también son funtores.
Ahora, si desea utilizar una función ordinaria como un funtor de for_each
, se vería algo como lo siguiente
inline void do_something(int &i) { /* do something */ }
int main() {
int array[10];
std::for_each(array, array + 10, &do_something);
}
En este caso la plantilla for_each
se instancia con argumentos [deducidas] <int *, void (*)(int &)>
. Tenga en cuenta que el valor real del functor en este caso es el puntero a la función &do_something
pasado como el argumento de la función. Desde el punto de vista de la función for_each
, este es un valor de tiempo de ejecución. Y dado que es un valor de tiempo de ejecución, las llamadas al funtor no pueden incluirse. (Al igual que en general es imposible hacer una llamada en línea a través de un puntero de función).
Pero si usamos un objeto función en su lugar, el código podría ser de la siguiente manera
struct do_something {
void operator()(int &i) { /* do something */ }
};
int main() {
int array[10];
std::for_each(array, array + 10, do_something());
}
En este caso la plantilla for_each
se instancia con argumentos [] deducidas <int *, do_something>
. Las llamadas al functor desde el interior for_each
se dirigirán al do_something::operator()
. El objetivo para la llamada es conocido y corregido en tiempo de compilación. Como la función de destino se conoce en tiempo de compilación, la llamada puede ser fácilmente inlineada.
En este último caso, por supuesto, también tenemos un valor de tiempo de ejecución pasado como argumento a for_each
. Es una instancia [posiblemente "ficticia" temporal] de la clase do_something
que creamos cuando llamamos al for_each
. Pero este valor de tiempo de ejecución no tiene ningún efecto en el destino para la llamada (a menos que el operator()
sea virtual), por lo que no afecta la alineación.
Pero la definición completa de ':: std :: for_each' está disponible para el compilador. Para un compilador inteligente, debería ver que se está llamando con un argumento particular que resulta ser la dirección de una función que se declaró en línea. El compilador debería poder alinear la llamada de función tan bien como si fuera un método de clase. – Omnifarious
@Omnifarious: en mi ejemplo, puede llamar a 'for_each' con diferentes funciones que tienen la firma' void (int &) 'y el compilador está obligado a usar la misma, una y única instanciación de' for_each': 'for_each
AnT
Por ejemplo (olvidarse de las plantillas), cuando tengo la función 'void foo (int)' en mi programa y la llamo varias veces como 'foo (1); foo (2); foo (3); 'Generalmente espero que el compilador genere un cuerpo de función para' foo' y ejecute el mismo cuerpo con diferentes argumentos: 1, 2 y 3. Si descubro que el compilador generó en cambio 3 cuerpos diferentes de 'foo' 'con constantes" en línea "1, 2 y 3, respectivamente, estaré desagradablemente sorprendido. Para el pequeño 'foo' que estaría bien (si está inline después de eso), pero para un' foo' más grande esto no es deseable. Lo que describes es esencialmente lo mismo. – AnT