2011-01-16 13 views
22

Estoy leyendo un libro sobre C++ y más precisamente sobre la sobrecarga del operador.C++ por qué el operador de asignación debe devolver un const ref para evitar (a = b) = c

El ejemplo es la siguiente:

const Array &Array::operator=(const Array &right) 
{ 
// check self-assignment 
// if not self- assignment do the copying 
return *this; //enables x=y=z 
} 

La explicación proporcionada por el libro de volver ref const en lugar de ref es evitar asignaciones tales como (x = y) = z. No entiendo por qué debemos evitar esto. Entiendo que x = y se evalúa primero en este ejemplo y dado que devuelve una referencia constante, la parte = z no se puede ejecutar. ¿Pero por qué?

+3

¿Qué libro? Eso parece una precaución innecesaria para mí. No me puedo imaginar a alguien escribiendo '(x = y) = z' - ¿por qué lo harían? Y sin los parens, 'x = y = z' se analiza como' x = (y = z) ', lo que tiene perfecto sentido, por lo que no hay ningún riesgo allí. –

+1

¿Pero por qué qué? ¿Por qué es una const ref? ¿Por qué se ejecuta en este orden? ¿Por qué no se puede asignar 'z' a (x = y)? – Lazarus

+16

@antronis: consigue un mejor libro de C++. – ybungalobill

Respuesta

24

(x=y) significa x.operator=(y), que devuelve el objeto x. Por lo tanto, (x=y)=z significa (x.operator=(y)).operator=(z). La expresión en pares establece x en y y devuelve x, y luego el bit externo establece x en z. No establece y en z como cabría esperar, y como lo hace la expresión x = y = z.

Este comportamiento es contrario a la intuición (todos deberían ser iguales después de la asignación, ¿no?); devolver una referencia constante lo hace imposible y evita el problema.

+2

Si bien el comportamiento parece contraintuitivo, no es un "problema" porque es algo que el programador habría hecho intencionalmente (por lo que utilizaron corchetes) – James

+18

¿Quién esperaría que '(x = y) = z' se establezca 'y' a' z'? Este es un "problema" muy artificial, y un libro de C++ no debería perder tiempo explicando las precauciones en su contra. '(x = 5) = z' no establece' 5' a 'z'. –

+0

Si espera que la asignación sea asociativa (como la comparación), esperaría que los tres sean iguales al final. Ingenuo, pero una interpretación razonable a primera vista. Estoy de acuerdo en que este formulario no se debe usar en general y no se debe enfatizar en el libro, pero es bueno ayudar al compilador a que * no * se pegue un tiro en el pie siempre que sea posible =) –

18

No hay necesidad de evitar esto, a menos que el libro esté dirigido a programadores que comúnmente escriben (x=y)=z cuando quieren decir x=y=z. En la práctica, nadie en su sano juicio escribe eso, por lo que la precaución es completamente innecesaria. También prohíbe algunos otros constructos escuetos, como (x=y).nonConstMember(), que casi nadie escribe, pero que podrían ser útiles en algunos contextos (aunque no deberían utilizarse en exceso).

@ybungalobill es correcto, consigue un mejor libro.

+9

+1. Un programador "accidentalmente" escribiendo '(x = y) = z' parece tan probable (y necesario evitarlo) como un programador que accidentalmente escribe' x = y + sistema ("rm -rf /") 'cuando quisieron decir 'x = y'. –

+0

@j_random_hacker suena posible en un "¡Vaya, ventana equivocada!" escenario ... – Calimo

+1

Pero he visto 'if ((x = y) = z) ...' cuando el autor quería 'if ((x = y) == z) ...'. –

10

Por lo que sé, los operadores de asignación no devuelven referencias de referencias en C++ idiomático. Los tipos estándar tampoco devuelven referencias a const.

std::string a, b, c; 
(a = b).clear(); // no objection from compiler 

Todos mis operadores de asignación personalizados han devuelto una referencia no constante.

En caso de duda, consulte la Biblioteca estándar. No es perfecto, pero definitivamente hace cosas básicas como esta correctas.

+0

La gente de Boost comúnmente devuelve referencias mutables también. –

3

Me gustaría ver el comportamiento de los tipos incorporados.

Al definir sus propios tipos, es preferible que los operadores se comporten de la misma manera que los tipos incorporados. Esto permite la fácil adopción de sus clases sin tener que profundizar en su código para ver por qué se comportan de manera diferente a lo esperado.

Así que si miramos los números enteros:

int main() 
{ 
    int x = 5; 
    int y = 6; 
    int z = 7; 

    (x = y) = z; 
    std::cout << x << " " << y << " " << z << "\n"; 
} 

Esto funciona con y sin cambios y X se asigna 7. En el código que cabe esperar de su operador de asignación para trabajar de la misma manera. La definición operador de asignación estándar:

Array& Array::operator=(Array const& rhs) 
{ 
    /* STUFF */ 
    return *this; 
} 

debe hacer eso muy bien (suponiendo/* * LA MATERIA/es correcta).

1

La única razón que puedo ver es que este libro fue escrito para explicar a los programadores de C++ a C (o por un autor cuya comprensión de C es mejor que la comprensión de C++). Porque para un programador en C, la expresión (x = y) = z no es válida para los tipos incorporados, y probablemente intente obtener el mismo comportamiento con sus tipos definidos por el usuario.

Sin embargo, C y C++ son idiomas diferentes, y en C++ la expresión (x = y) = z es válida incluso para tipos incorporados. Por lo tanto, si desea tener el mismo comportamiento para los tipos definidos por el usuario, debe devolver una referencia no constante en el operator =.

Le aconsejo que obtenga un mejor libro, uno que no haga la confusión entre C y C++. No son los mismos idiomas, incluso si provienen de una base común.

Cuestiones relacionadas