2010-07-22 11 views
14

Siempre me ha enseñado que los tipos no primitivos deben ser pasados ​​por referencia const en lugar de por su valor siempre que sea posible, es decir:¿Deberían pasar estructuras simples pequeñas por referencia constante?

void foo(std::string str);//bad 
void foo(const std::string &str);//good 

Pero estaba pensando hoy que tal vez en realidad algunos tipos definidos por el usuario simples pueden ser en realidad mejor pasa por valor, por ejemplo:

class Vector2 
{ 
public: 
    float x, y; 
    ...constructors, operators overloads, utility methods, etc... 
}; 

void foo(Vector2 pos); 
void foo(const Vector2 &pos);//is this really better than by value? 
void foo(float x, float y);//after all isn't by value effectively doing this? 

Mi pensamiento es que al pasar el Vector2 por referencia, en realidad es más caro que el paso por valor ya que el compilador está en usar un puntero y la eliminación de referencias para acceder a la const Vector2 & pos ¿versión?

¿Es este el caso? ¿Los objetos simples se pasan mejor por valor? ¿Dónde debería dibujarse la línea?

+0

Tenga en cuenta que si su tipo de clase tiene constructores declarados por el usuario, no es POD. –

+6

La regla general: Pase por const-reference, a menos que 'sizeof (T) <= sizeof (void *)' o 'T' sea primitivo. – GManNickG

+2

@GMan, me ganaste (y +1). Aunque, generalizaría que probablemente sea menor o igual que el tamaño de palabra del procesador. Y, uno debe verificar las especificaciones del procesador para descubrir el tamaño óptimo. Si no me equivoco, los compiladores podrían optimizar una constante de referencia a un valor de aprobación en algunos casos, independientemente del tamaño. Me imagino que esto es especialmente cierto en C++ 0x dada la nueva semántica de movimiento. –

Respuesta

7

Sí, los objetos simples se deben pasar por valor. La línea debe dibujarse según la arquitectura. En caso de duda, perfil.

+0

+1 para el perfilado: es la única forma de saber cuál es mejor –

1

Pasar por referencia const evita la construcción de un nuevo objeto. Si su constructor no es trivial, por ejemplo, asigna memoria, será mucho mejor pasando por referencia constante que por valor.

En el mundo C#, usted toma esta decisión eligiendo si desea hacer de algo una clase o una estructura. Las clases usan semántica de referencia mientras que las estructuras usan semántica de valores. Todo lo que he leído dice que, en general, debe elegir convertir todo en una clase a menos que sea muy pequeño, es decir, del orden de 16 bytes. Si está buscando un punto de corte para cuándo pasar por valor frente a la referencia constante en C++, 16 bytes parece un umbral razonable.

+1

Los tipos de POD no tienen constructores de copia no triviales (o incluso constructores o destructores predeterminados no triviales). –

+2

El OP no dice explícitamente el tipo de POD. Dijo ** simple ** tipo definido por el usuario y luego dio un ejemplo que indicaba que tenía un constructor. –

0

Mi pensamiento es que al pasar el Vector2 por referencia, en realidad es más caro que el paso por valor ya que el compilador está en usar un puntero y la eliminación de referencias para acceder a la const Vector2 & pos versión

Sería cierto si pasó un objeto donde size_of(object) < size_of(pointer_to_object). Por ejemplo, char const& podría ser más costoso que char

+2

También puede ser cierto si la desreferencia repetida dará como resultado más sobrecarga de la que se ahorra al no pasar la misma cantidad de datos. O si la referencia impide optimizaciones debido a problemas de aliasing. O si la referencia perjudica a tu localidad. –

1

Solo como una cuestión de política paso cualquier objeto que no sea un tipo de datos C básico por referencia. Es mucho más fácil de recordar de esa manera.

1

Se puede encontrar un análisis más antiguo, aunque lúcido, de las entradas y salidas de los parámetros en http://www.ddj.com/184403855. Por supuesto, C++ 0x obvia muchos de estos problemas con la semántica de movimiento, pero el documento proporciona una gran cantidad de justificación de por qué son deseables la semántica de movimiento.

1

Uno de los factores que no he mencionado es lo que la rutina va a hacer con el valor pasado. A menos que la rutina se amplíe en línea, la manipulación de los datos que se pasan por referencia requerirá más código que la manipulación de datos que se pasan por valor. Si se accede a los campos de la estructura pasada en menos de una vez cada uno, esta sobrecarga adicional será pequeña en comparación con la sobrecarga de copiar la estructura. Si se accede a ellos en promedio muchas veces cada uno, probablemente sería mejor tenerlos en la pila. Si la interfaz ya está configurada como pasada por referencia para una estructura a la que se accede en gran medida, puede tener sentido que la rutina llamada copie todas las partes que sean de su interés. Por otro lado, si la rutina invocada va a copiar gran parte o la totalidad de una estructura de todos modos, también puede pasarse por valor.

Cuestiones relacionadas