operator - valor de retorno de la sobrecarga del operador en C++
operator[] c++ (5)
Tengo una pregunta sobre el valor de retorno de la sobrecarga de operadores en C ++. En general, encontré dos casos, uno es de retorno por valor y uno es de retorno por referencia. Entonces, ¿cuál es la regla subyacente de eso? Especialmente en el caso cuando puede utilizar el operador de forma continua, como cout<<x<<y
.
Por ejemplo, al implementar una operación + "cadena + (cadena)". ¿Cómo devolvería el valor de retorno, por ref o por val.
Algunos operadores devuelven por valor, otros 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 un std::ostream
, y la inserción de datos en el flujo 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;
}
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
. Como 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.
A la inversa, para "cadena + cadena", el resultado es una cadena nueva (ambas cadenas originales no han sido modificadas), por lo que debe devolver por valor (de lo contrario, devolvería una referencia a un temporal, que es un comportamiento indefinido).
Dependiendo del operador puede que tenga que devolver por valor.
Sin embargo, cuando se pueden usar ambos, como en el operador + = podría considerar lo siguiente:
- Si sus objetos son inmutables, probablemente sea mejor devolverlos por valor.
- Si sus objetos son mutables, probablemente sea mejor regresar por referencia.
Para intentar responder a su pregunta con respecto a las cadenas, el operador + () para cadenas se implementa casi siempre como una función gratuita (no miembro) para que se puedan realizar conversiones implícitas en cualquiera de los parámetros. Eso es para que puedas decir cosas como:
string s1 = "bar";
string s2 = "foo" + s1;
Dado eso, y que podemos ver que ninguno de los parámetros se puede cambiar, se debe declarar como:
RETURN_TYPE operator +( const string & a, const string & b );
Ignoramos el RETURN_TYPE por el momento. Como no podemos devolver ninguno de los parámetros (porque no podemos cambiarlos), la implementació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 de RETURN_TYPE una referencia, devolveremos una referencia a un objeto local, que es un conocido no-no, ya que el objeto local no existe fuera de la función. Así que 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;
}
Por lo general, regresa por referencia en una operación que cambia el valor de las cosas en las que está funcionando, como =
o +=
. Todas las demás operaciones se devuelven por valor.
Sin embargo, esta es más una regla de oro. Puede diseñar su operador de cualquier manera.
Si desea que la sobrecarga de su operador se comporte como el operador integrado, entonces la regla es bastante simple; el estándar define exactamente cómo se comportan los operadores incorporados e indicará si el resultado de un incorporado es un valor de rvalue
o un valor de lvalue
.
La regla que debes usar es:
- Si el operador incorporado devuelve un valor de
rvalue
, su sobrecarga debe devolver una referencia. - Si el incorporado devuelve un
lvalue
entonces su sobrecarga debe devolver un valor.
Sin embargo, no es necesario que su sobrecarga devuelva 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 de otra manera.
Por ejemplo, KennyTM observó 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 cómo funcionan las funciones integradas. Pero los diseñadores de la interfaz de flujo hicieron esto para poder encadenar la E / S de flujo.