c++ - define - ¿No es innecesario el modificador const?
const en c++> (5)
El modificador const en el valor de retorno no es necesario y puede obstaculizar la semántica del movimiento. La forma preferida de evitar la asignación de valores en C ++ 11 es usar " ref-qualifiers ".
struct Rational
{
Rational & operator=( Rational other ) &; // can only be called on lvalues
};
Rational operator*( Rational const & lhs, Rational const & rhs );
Rational a, b, c;
(a * b) = c; // error
Esta pregunta ya tiene una respuesta aquí:
- ¿Cómo se puede asignar un objeto devuelto? 1 respuesta
El ítem 3 " Effective C ++ " dice "Use const siempre que sea posible", y da un ejemplo como:
const Rational operator*(const Rational& lhs,
const Rational& rhs);
para evitar que los clientes puedan cometer atrocidades como esta:
Rational a, b, c;
...
(a * b) = c; // invoke operator= on the result of a*b!
Pero, ¿no es el valor de retorno sin referencia de las funciones ya un valor r ? Entonces, ¿por qué molestarse en hacer esto?
El punto es que para los tipos de clase (pero no para los tipos integrados), a = b
es solo una abreviatura de a.operator=(b)
, donde operator = es una función miembro. Y las funciones de miembro se pueden invocar en valores r (a * b)
creados por Rational::operator*
. Para aplicar una semántica similar a la de los valores acumulados (" hacer lo que hacen los enteros "), algunos autores (incluido Meyers) recomendaron en C ++ 98 devolver por const-rvalue para las clases con dichos operadores.
Sin embargo, en C ++ 11 regresar por const-rvalue es una mala idea, ya que inhibirá la semántica de movimiento porque los valores de const no se pueden unir a T&&
.
En sus notas. Una visión general del nuevo C ++ (C ++ 11) , Scott Meyers da precisamente el mismo ejemplo de su libro anterior, y concluye que ahora se considera un diseño deficiente para agregar el valor de retorno constante. La firma recomendada es ahora
Rational operator*(const Rational& lhs, const Rational& rhs);
ACTUALIZACIÓN: como implícita @ JohannesSchaub-litb en los comentarios, en C ++ 11 también puede usar un calificador de referencia en el operador de asignación para que solo acepte lvalues como su argumento de la izquierda (es decir, el *this
puntero, que es por qué esta característica también se conoce como "referencias rvalue para * esto"). Necesitarás g ++> = 4.8.1 (recién lanzado) o Clang> = 2.9 para hacer uso de él.
Porque es posible que tenga la intención de escribir (a * b) == c
vez, es decir,
if ((a * b) = (c + d)) // executes if c+d is true
Pero querías
if ((a * b) == (c + d)) // executes if they''re equal
Quizás esto me cueste puntos de representante, pero no estoy de acuerdo. No modifique los tipos de devolución esperados de los operadores sobrecargados, ya que molestará a los usuarios de su clase. es decir, uso
Rational operator*(const Rational& lhs, const Rational& rhs);
(Por supuesto, la const
de parámetros es una buena práctica, y tener parámetros de referencia constantes es aún mejor, ya que significa que el compilador no realizará copias profundas. Pero no tiene un valor de retorno de referencia constante en este caso, aunque sí lo obtendrá una referencia colgante que es catastrófica. Pero tenga en cuenta que, a veces, tomar una referencia es más lento que un valor de pase. Creo que las double
e int
entran en esa categoría en muchas plataformas).
Supongo que lo que le gustaría hacer de acuerdo con su pregunta es declarar el operador correspondiente = privado de modo que ya no sea accesible.
por lo tanto, le gustaría sobrecargar la firma que coincide (a*b) = c
. Estoy de acuerdo en que la parte izquierda es una expresión y, por lo tanto, un valor de referencia sería una mejor coincidencia. sin embargo, está ignorando el hecho de que este es el valor de retorno de la función si sobrecarga la función para devolver un valor r, el compilador se quejará de una sobrecarga no válida ya que las reglas de sobrecarga no consideran los valores de retorno.
Como se indica here la sobrecarga del operador para la asignación siempre es una definición de clase interna. si hubiera una firma no miembro como void operator=(foo assignee, const foo& assigner);
la resolución de sobrecarga podría coincidir con la primera parte como valor r (luego puede eliminarla o declararla privada).
Entonces puedes elegir entre dos mundos:
- vivir con el hecho de que los usuarios pueden escribir cosas estúpidas como
(a*b) = c
que no está mal, pero almacena el valor de c en un tempo inaccesible - utilice la firma
const foo operator*(const foo& lhs, const foo& rhs)
que disuada la(a*b) = c
y la semántica del movimiento de sacrificio
código
#include <utility>
class foo {
public:
foo() = default;
foo(const foo& f) = default;
foo operator*(const foo& rhs) const {
foo tmp;
return std::move(tmp);
}
foo operator=(const foo& op) const {
return op;
}
private:
// doesn''t compile because overloading doesn''t consider return values.
// conflicts with foo operator=(const foo& op) const;
foo && operator=(const foo& op) const;
};
int main ( int argc, char **argv ) {
foo t2,t1;
foo t3 = t2*t1;
foo t4;
(t2 * t1) = t4;
return 0;
}