2009-10-04 8 views
6

Cuando leo litb answer to this question, me enteré de que al pasar una matriz por referencia nos permite obtener su tamaño. Acabo de jugar poco con el código, y trató de pasar a una "función" de referencia y sorprendente (al menos para mí), este código se compila:¿Podría alguien explicar la diferencia entre una "referencia" y un "puntero" en este caso?

void execute(void (&func)()) // func is passed by reference! 
{ 
    func(); 
} 

¿Hay alguna diferencia entre la última función, y éste :

void execute(void (*func)()) // func is passed by pointer! 
{ 
    func(); 
} 

I trataron usando VC2008, y produce una salida diferente en cada caso. Lo extraño es que el compilador optimiza el código mejor en caso de un puntero de función:

void print() 
{ 
    std::cout << "Hello References!"; 
} 
void execute(void (&func)()) // optimized 
{ 
    func(); 
} 
int main() 
{ 
    00291020 call print (291000h) 
} 
========================================= 
// In this case, the compiler removes all function calls in the code! 
void print() // optimized! 
{ 
    std::cout << "Hello Pointers!"; 
} 
void execute(void (*func)()) // optimized 
{ 
    func(); 
} 
int main() 
{ 
    002F1005 push offset string "Hello References!" (2F2124h) 
    002F100A push eax 
    002F100B call std::operator<<<std::char_traits<char> > (2F1150h) 
} 

Tiene que haber una diferencia, aunque yo no lo veo, ¿verdad?

Nota: el código se compiló utilizando VC2008, con /O2 y /Ot encendido.


EDITAR :: estoy realmente interesado acerca de cualquier diferencia entre las referencias de función y los punteros de función. Examiné el código ensamblador producido solo para ver cómo se traduce en cada caso.

+0

si son sólo interesado en el comportamiento o en los optimizadores información general de referencia de funciones? –

+0

@litb De hecho, me sorprende que pueda pasar una función por referencia. Sería genial si tiene una explicación sobre la diferencia en la funcionalidad u otros aspectos de los indicadores de función :) – AraK

Respuesta

3

Por la diferencia de idioma (manteniendo sólo las declaraciones de funciones a continuación, ya que eso es lo que es importante solamente)

void execute(void (&func)()); 

void g(); 
int main() { 
    void (*fp)() = g; 
    execute(fp); // doesn't work 
    execute(&g); // doesn't work either 
    execute(g); // works 
} 

no funciona, ya que quiere una función, no un puntero de función. Por la misma razón que la respuesta de matriz rechaza un puntero, esto también rechaza un puntero. Tienes que pasar "g" directamente.

Para las plantillas, importa demasiado

template<typename T> 
void execute(T &t) { T u = t; u(); } 

template<typename T> 
void execute(T t) { T u = t; u(); } 

Los dos son muy diferentes entre sí. Si lo llama con execute(g); como arriba, el primero intentará declarar una función e inicializarla con t (referencia a g). La función generada se vería así

void execute(void(&t)()) { void u() = t; u(); } 

Ahora se puede inicializar referencias y punteros a funciones, pero, por supuesto, no es en sí funciones. En la segunda definición, T se deduce a un tipo de puntero de función por deducción de argumento de plantilla, y pasar una función lo convertirá implícitamente en ese tipo de parámetro de puntero. Entonces todo irá bien.


no sé qué MSVC los trata de manera diferente para procesos en línea - pero también sospecho que es porque las referencias de función aparecen con menos frecuencia.

+0

¿Qué pasa con 'execute (* fp);', sin embargo, el cuarto caso faltante :-) –

3

No es una expresión tan común, por lo que podría ser que el equipo de VS no haya agregado una regla para optimizarla.

3

creo que es debido a la Norma C++ 4.3:

Un lvalue de tipo de función T puede ser convertido a un valor p de tipo “puntero a T.” El resultado es un puntero a la función .

+0

Cita interesante. ¿No significa eso después de convertir la referencia de función, básicamente el mismo código debe producirse en ambos casos? Sé que esto es un detalle de implementación, pero ¿cómo podría el compilador optimizar el puntero y no la referencia (si se convierte)? – AraK

-2

La diferencia entre una referencia (&) y un puntero (*) es que la referencia proporciona la dirección de la variable o la ubicación, y el puntero apunta a la ubicación en la memoria de la dirección almacenada en él.

int *pointer; 
int variable; 

pointer = &variable; // assigning the address of variable to pointer 

variable = 53; // value of variable 

cout << *pointer; // This should output the value of the address where is pointing, in this 
        // case 53, that is the value of variable to where is pointing. 

Podemos concluir que el (& variable) tiene la dirección de la localización de memoria y * anyname apunta a la dirección almacenada en su memoria ...

+0

_ "la referencia proporciona la dirección de la variable o la ubicación, y el puntero apunta a la ubicación en la memoria de la dirección almacenada en él. "_ eh ... creo que estás confundiendo referencias para el operador de dirección. –

Cuestiones relacionadas