2011-05-20 11 views
16
template <typename TAG> 
fn(int left, TAG, int right) 
{ 
} 

fn(0, some_type_tag(), 1); 
/* or */ 
fn(0,int(), 1); // where the primitive, int, is not empty. 

EDIT: Hay dos puntos de vista a esta pregunta.¿Se pasa realmente un parámetro sin nombre durante una llamada a función?

  1. Declaración de funciones vs definición. La declaración podría no nombrar el parámetro, pero la declaración podría hacerlo. Esta no es la perspectiva de interés.
  2. La perspectiva de la plantilla, específicamente en la metaprogramación. El parámetro en cuestión es una etiqueta utilizada para extraer una metaestructura de un rasgo. Esta es la razón por la cual el parámetro no tiene nombre, solo me importa la información en tiempo de compilación, el tipo de etiqueta.

/EDIT

Mis etiquetas son generalmente vacías-estructuras, sin embargo, en algunas partes de mi código que son typedefs de types.So primitiva, me interesa saber si los compiladores modernos realmente se apruebe un parámetro. Esto tiene dos aspectos.

  1. Dimensionamiento de la pila, teniendo en cuenta el tamaño del tipo de parámetro sin nombre.
  2. Construyendo realmente la pila con el valor pasado.

Permite mantener a gcc 4.5 y msvc 2008+

+1

Los resultados variarán según los compiladores y los niveles de optimización. Tendrás que probarlo. –

+0

Tengo 10 preguntas como esta al día, si experimenté en todo lo que no haría ningún trabajo;), califico la pregunta con los dos compiladores que me interesan. –

+2

@Hassan: Creo que han terminado antes de escribir una prueba simple en lugar de escribir toda la pregunta :) –

Respuesta

6

Realmente es una pregunta interesante.

Antes que nada, tenga en cuenta que estamos en un lenguaje imperativo, lo que significa que cuando pide algo (incluso inútil, como construir un objeto sin usar) el compilador debe cumplir a menos que se le ocurra una forma equivalente . Básicamente, podría eludir el parámetro si pudiera demostrar que hacerlo no cambiaría el significado del programa.

Cuando se escribe una llamada a la función, pueden ocurrir dos cosas (al final):

  • ya sea que se colocarán en línea
  • o una call es en realidad emitida

Si es en línea, entonces no se pasa ningún parámetro, lo que significa que los objetos no utilizados se pueden eliminar (y ni siquiera crear) si el compilador puede demostrar que los constructores y destructores implicados no realizan ningún significado t trabajo. Funciona bien para las estructuras de etiquetas.

Cuando se emite una llamada, se emite con una convención de llamada específica. Cada compilador tiene su propio conjunto de convenciones de llamadas que especifican cómo pasar los diversos argumentos (puntero this, etc.), generalmente tratando de aprovechar los registros disponibles.

Dado que sólo se utiliza la declaración de la función para determinar la convención de llamada (modelo de compilación independiente), entonces es necesario para realmente pasar el objeto ...

Sin embargo, si estamos hablando de una estructura vacía , sin método y sin estado, entonces esto es solo una memoria no inicializada. No debería costar mucho, pero requiere espacio en la pila (al menos, reservándolo).

demostración utilizando la prueba de audición llvm:

struct tag {}; 

inline int useless(int i, tag) { return i; } 

void use(tag); 

int main() { 
    use(tag()); 
    return useless(0, tag()); 
} 

Da:

%struct.tag = type <{ i8 }> 

define i32 @main() { 
entry: 
    ; allocate space on the stack for `tag` 
    %0 = alloca %struct.tag, align 8    ; <%struct.tag*> [#uses=2] 

    ; get %0 address 
    %1 = getelementptr inbounds %struct.tag* %0, i64 0, i32 0 ; <i8*> [#uses=1] 

    ; 0 initialize the space used for %0 
    store i8 0, i8* %1, align 8 

    ; call the use function and pass %0 by value 
    call void @_Z3use3tag(%struct.tag* byval %0) 
    ret i32 0 
} 

declare void @_Z3use3tag(%struct.tag* byval) 

Nota:

  • cómo se eliminó la llamada a useless, y ningún argumento está construido para ello
  • cómo llamar a use no puede ser eliminado, y por lo tanto el espacio se asigna para el temporal (espero que las nuevas versiones no 0-initialize la memoria)
+0

El nivel correcto de estilo, autoridad y tono que estaba buscando: D –

+0

@Hassan: Estoy aprendiendo tanto como usted y se me conoce por haber perdido la atención, por lo que definitivamente es autoritario^^ –

2

Buena pregunta, pero usted tendrá que probar en su compilador. En teoría, si no se usa un parámetro, no tiene que asignarse en la pila. Sin embargo, las personas que llaman deben saber cómo llamarlo, así que supongo que el elemento realmente se asigna en la pila.

12

C++ tiene traducción separada. Como el parámetro se puede nombrar en la declaración pero no en la definición de la función y viceversa, generalmente no hay forma de que el compilador sepa si es seguro omitir el argumento de la función. Cuando todo está en la misma unidad de traducción, todo podría estar en línea y el nombre del argumento es completamente irrelevante para la optimización.

[Agregado]

La traducción independiente puede no importar a este caso específico, sino un constructor compilador que añadiría una optimización de este tipo debe cuidar. No van a poner tales optimizaciones si rompe el código perfectamente válido.

En cuanto a las plantillas, es necesario que el tipo de una función de plantilla sea igual al tipo de una función que no sea de plantilla, sino que es imposible tomar su dirección y asignarla a un puntero de función. Nuevamente, debe tener en cuenta la traducción por separado. El hecho de que no tome la dirección de foo<int> en esta TU no significa que no lo hará en otra.

+1

+1 para traducción por separado – sehe

+0

El compilador genera una llamada de función si se toma una dirección. Sin embargo, no usará (o no debería) esta función nueva a menos que sea necesario. eso todavía deja la clase de función que está bajo escrutinio de mi caso de uso = D. Entonces, tal vez podemos suponer que si se toma una dirección, el compilador crea una llamada de función que toma en cuenta el parámetro de etiqueta. –

+0

Eso aún deja un problema: si _una_ persona que llama toma en cuenta el parámetro, el llamado debe, lo que a su vez significa que _todos los llamantes deben hacerlo. – MSalters

7

Si el parámetro tiene o no nombre no tiene ningún efecto sobre la firma de la función, y el compilador debe pasarla. Considere que un parámetro sin nombre en la declaración de una función podría nombrarse en la definición.

Ahora, en el caso particular de las plantillas como la anterior, es probable que el compilador alinee el código, en cuyo caso no se pasarán argumentos, y el argumento sin nombre no tendrá ningún efecto.

Si lo que estamos tratando de hacer es etiquetar a resolver a diferentes sobrecargas, siempre se puede recurrir a un puntero, por lo que incluso si se pasa, el costo será mínimo.

+0

+1 por implicaciones en línea – sehe

+0

gracias Estoy realmente interesado en la perspectiva de la plantilla. He calificado más la pregunta para reflejar esto. Generalmente, como podría usar tipos primitivos como etiquetas, en lugar de estructuras vacías, la sobrecarga de tamaño no debería ser tan mala, si uso punteros pagaré la sobrecarga de 8 bytes para el código de 64 bits en todo momento: D. Aunque supongo que 8 bytes del espacio de la pila de llamada no tienen ninguna implicación para el rendimiento. –

+0

@Hassan Syed: si usa 64 bits, es probable que la convención de llamadas haga un uso extenso de los registros, lo que a su vez significará que para todo lo que se ajuste al registro, el costo será restablecerlo (en ese caso no en línea)) –

Cuestiones relacionadas