2010-02-25 32 views
37

Tengo una pregunta sobre el valor de retorno de la sobrecarga del operador en C++. En general, encontré dos casos, uno es retorno por valor, y uno es retorno por referencia. Entonces, ¿cuál es la regla subyacente de eso? Especialmente en el caso en que puede usar el operador continuamente, como cout<<x<<y.valor de retorno de la sobrecarga del operador en C++

Por ejemplo, al implementar una operación + "string + (string)". cómo devolverías el valor de retorno, por ref o por val.

Respuesta

56

Algunos operadores devuelven valor, algunos por referencia. En general, un operador cuyo resultado es un nuevo valor (como +, -, etc.) debe devolver el nuevo valor por valor, y un operador cuyo resultado es un valor existente, pero modificado (como < <, >>, + =, - =, etc.), debe devolver una referencia al valor modificado.

Por ejemplo, cout es una std::ostream, y la inserción de datos en la corriente es una operación de modificación, por lo que para implementar el operador << para insertar en un ostream, el operador se define así:

std::ostream& operator<< (std::ostream& lhs, const MyType& rhs) 
{ 
    // Do whatever to put the contents of the rhs object into the lhs stream 
    return lhs; 
} 

Este De esta manera, cuando tiene una declaración compuesta como cout << x << y, primero se evalúa la sub-expresión cout << x y luego se evalúa la expresión [result of cout << x ] << y. Dado que el operador << en x devuelve una referencia a cout, la expresión [result of cout << x ] << y es equivalente a cout << y, como se esperaba.

Por el contrario, para "string + string", el resultado es una nueva cadena (ambas cadenas originales no se modifican), por lo que debe devolver por valor (de lo contrario, devolvería una referencia a un comportamiento temporal, indefinido) .

+9

Lo que va de la mano con "si es const, devuelve un valor, si no es const, devuelve una referencia". – GManNickG

+10

Técnicamente '<<' no modifica el valor existente. Es solo un operador de cambio a la izquierda. Es la interfaz de transmisión que rompe las reglas. – kennytm

+0

@GMan correcto, aunque eso no cubre los casos (como 'operator <<') donde la sobrecarga se implementa como no miembro. –

2

Según el operador, puede que tenga que devolver por valor.

Cuando ambos pueden ser utilizados sin embargo, al igual que en el operador + = usted podría considerar lo siguiente:

  • Si los objetos son inmutables es probable que sea mejor volver por valor.
  • Si sus objetos son mutables, probablemente sea mejor devolverlos por referencia.
+0

El propósito de 'operator + =' es mutar el objeto. No debe invocarse sobre objetos inmutables. – army007

2

Normalmente se devuelve por referencia en una operación que cambia el valor de las cosas en las que está operando, como = o +=. Todas las otras operaciones son devueltas por valor.

Esto es más una regla de oro, sin embargo. Puede diseñar su operador de cualquier manera.

4

Si desea que la sobrecarga de su operador se comporte como el operador incorporado, entonces la regla es bastante simple; el estándar define exactamente cómo se comportan los operadores integrados e indicará si el resultado de un built-in es un rvalue o un lvalue.

La regla que debe utilizar es:

  • si el operador incorporado devuelve un rvalue entonces su sobrecarga debe devolver una referencia
  • si el built-in devuelve un lvalue entonces su sobrecarga debe devolver una valor

Sin embargo, su sobrecarga no es necesaria para devolver el mismo tipo de resultado que el incorporado, aunque eso es lo que debe hacer a menos que tenga una buena razón para hacerlo.

Por ejemplo, KennyTM señaló en un comentario a otra respuesta que las sobrecargas de flujo para los operadores << y >> devuelven una referencia al operando de la izquierda, que no es así como funcionan los integradores. Pero los diseñadores de la interfaz de transmisión hicieron esto para poder encadenar E/S de transmisión.

+0

Como referencia, los operadores incorporados están en § 13.6 del estándar C++ 11 del borrador N3337, que es la única versión del estándar que tengo a mano. –

12

Para intentar responder a su pregunta con respecto a las cadenas, el operador +() para cadenas casi siempre se implementa como una función libre (no miembro) para que se puedan realizar conversiones implícitas en cualquiera de los parámetros. Eso es por lo que puede decir cosas como:

string s1 = "bar"; 
string s2 = "foo" + s1; 

Teniendo en cuenta que, y que podemos ver que ni el parámetro puede ser cambiado, debe declararse como:

RETURN_TYPE operator +(const string & a, const string & b); 

ignoramos la return_type para el momento. Ya que no podemos devolver cualquiera de los parámetros (porque no podemos cambiarlos), la aplicación debe crear un nuevo valor concatenado:

RETURN_TYPE operator +(const string & a, const string & b) { 
    string newval = a; 
    newval += b; // a common implementation 
    return newval; 
} 

Ahora si hacemos return_type una referencia, que volveremos una referencia a un local de objeto, que es un no-no conocido ya que el objeto local no existe fuera de la función. Entonces, nuestra única opción es devolver un valor, es decir, una copia:

string operator +(const string & a, const string & b) { 
    string newval = a; 
    newval += b; // a common implementation 
    return newval; 
} 
+0

Entonces, para liberar el resultado asignado, ¿corregí eso porque la invocación del Operador es en efecto una declaración, el valor de retorno anónimo va en la pila, y entonces se libera cuando regresa la función contenedora? –