En el Effective C++elemento 03, utilice constante siempre que sea posible.
class Bigint
{
int _data[MAXLEN];
//...
public:
int& operator[](const int index) { return _data[index]; }
const int operator[](const int index) const { return _data[index]; }
//...
};
const int operator[]hace la diferencia de int& operator[].
Pero que pasa:
int foo() { }
y
const int foo() { }
Parece que son iguales.
Mi pregunta es, ¿por qué usamos en const int operator[](const int index) constlugar de int operator[](const int index) const?

int operator[](int i)versusconst int operator[](const int i).Bigint&oa const Bigint&.const int idiscusión.Respuestas:
Se ignoran los calificadores de cv de nivel superior en tipos de devolución de tipos que no son de clase. Lo que significa que incluso si escribe:
int const foo();El tipo de retorno es
int. Si el tipo de retorno es una referencia, por supuesto,constya no es el nivel superior y la distinción entre:int& operator[]( int index );y
int const& operator[]( int index ) const;es significante. (Tenga en cuenta también que en las declaraciones de funciones, como las anteriores, también se ignoran los calificadores cv de nivel superior).
La distinción también es relevante para los valores de retorno de tipo de clase: si regresa
T const, entonces la persona que llama no puede llamar a funciones no constantes en el valor devuelto, por ejemplo:class Test { public: void f(); void g() const; }; Test ff(); Test const gg(); ff().f(); // legal ff().g(); // legal gg().f(); // **illegal** gg().g(); // legalfuente
()despuésffygg. Los agregaré. Gracias por la corrección.constes útil, sin embargo, muchas personas parecen pasarla por alto o insistir en que devolver unconstvalor no tiene ninguna utilidad.int const, por ejemplo, es equivalente a una llamada de esa función modificada para regresarint. Standardese en C ++ 11 §3.10 / 4, “Los valores de clase pueden tener tipos calificados por cv; los prvalues que no son de clase siempre tienen tipos cv-no calificados ”.Debe distinguir claramente entre el uso constante que se aplica a los valores de retorno, los parámetros y la función en sí.
Valores devueltos
Considere
const std::string SomeMethod() const. No permitirá(std::string&&)que se use la función, ya que espera rvalue no constante. En otras palabras, la cadena devuelta siempre se copiará.constprotege el objeto devuelto de ser modificado.Parámetros
Función en sí
constal final, solo puede ejecutar otrasconstfunciones y no puede modificar o permitir la modificación de los datos de la clase. Por lo tanto, si regresa por referencia, la referencia devuelta debe ser constante. Solo las funciones const se pueden llamar en un objeto o una referencia a un objeto que es const en sí mismo. También se pueden cambiar los campos mutables.thisreferenciaT const*. La función siempre puedeconst_castthis, pero, por supuesto, esto no se debe hacer y se considera inseguro.Conclusión
Si su método no modifica y nunca modificará las variables de clase, márquelo como constante y asegúrese de cumplir con todos los criterios necesarios. Permitirá que se escriba un código más limpio, manteniéndolo const-correcto . Sin embargo, poner en
consttodas partes sin pensar en ello ciertamente no es el camino a seguir.fuente
constse ignora para los tipos que no son de clase . Importa para los tipos de clases. Y para los parámetros pasados por valor, el nivel superiorconstse ignora en las declaraciones de funciones. Solo tiene significado en la definición.consta una función: modifica el tipo dethis, que esT const*, en lugar deT*. Todos los demás efectos se derivan de esto. (Y no evita todas las modificaciones del objeto: losmutabledatos aún se pueden cambiar, y la función puede desechar legalmente const y cambiar lo que desee).Hay poco valor en agregar
constcalificaciones a valores r que no son de referencia / no punteros, y no tiene sentido agregarlos a los valores integrados.En el caso de los tipos definidos por el usuario , una
constcalificación evitará que las personas que llaman invoquen unaconstfunción no miembro en el objeto devuelto. Por ejemplo, dadoconst std::string foo(); std::string bar();luego
foo().resize(42);estaría prohibido, mientras
bar().resize(4711);estaría permitido.
Para integradas como
int, esto no tiene ningún sentido, porque tales valores r no se pueden modificar de todos modos.(Sin embargo, recuerdo que Effective C ++ discutió cómo hacer el tipo de retorno de
operator=()unaconstreferencia, y esto es algo a considerar).Editar:
Parece que Scott dio ese consejo . Si es así, debido a las razones dadas anteriormente, lo encuentro cuestionable incluso para C ++ 98 y C ++ 03 . Para C ++ 11, lo considero claramente incorrecto , como parece haber descubierto el propio Scott. En las erratas para C ++ efectivo, 3ª ed. , escribe (o cita a otros que se quejaron):
Y después:
fuente
Es posible que pierda el sentido del consejo de Meyers. La diferencia esencial está en el
constmodificador del método.Este es un método no constante (observe no
constal final) lo que significa que puede modificar el estado de la clase.int& operator[](const int index)Este es un método constante (aviso
constal final)const int operator[](const int index) const¿Qué pasa con los tipos de parámetro y el valor de retorno? Hay una pequeña diferencia entre
intyconst int, pero no es relevante para el punto del consejo. A lo que debe prestar atención es que la sobrecarga no constante devuelve, loint&que significa que puede asignarle, por ejemplonum[i]=0, y la sobrecarga constante devuelve un valor no modificable (sin importar si el tipo de retorno esintoconst int).En mi opinión personal, si un objeto se pasa por valor , el
constmodificador es superfluo. Esta sintaxis es más corta y logra el mismoint& operator[](int index); int operator[](int index) const;fuente
constvalor.fooejemplo con eloperator[]ejemplo. Podría perder el punto de que elconstmodificador para el método marca la diferencia, no el tipo de retorno.La razón principal para devolver valores como const es que no puede decir algo como
foo() = 5;. En realidad, esto no es un problema con los tipos primitivos, ya que no se pueden asignar valores r de tipos primitivos, pero es un problema con los tipos definidos por el usuario (como(a + b) = c;, con una sobrecargaoperator+).Siempre he encontrado la justificación para eso bastante endeble. No se puede detener a alguien que tiene la intención de escribir un código incómodo, y este tipo particular de coerción no tiene ningún beneficio real en mi opinión.
Con C ++ 11, en realidad hay mucho daño que este idioma está haciendo: devolver valores como const evita las optimizaciones de movimiento y, por lo tanto, debe evitarse siempre que sea posible. Básicamente, ahora consideraría esto como un anti-patrón.
Aquí hay un artículo relacionado tangencialmente sobre C ++ 11.
fuente
Cuando se escribió ese libro, el consejo fue de poca utilidad, pero sirvió para evitar que el usuario escribiera, por ejemplo,
foo() = 42;y esperara que cambiara algo persistente.En el caso de
operator[], esto puede ser un poco confuso si no proporciona también una noconstsobrecarga que devuelva una noconstreferencia, aunque quizás pueda evitar esa confusión devolviendo unaconstreferencia o un objeto proxy en lugar de un valor.En estos días, es un mal consejo, ya que le impide vincular el resultado a una
constreferencia (no ) de valor.(Como se señaló en los comentarios, la pregunta es discutible cuando se devuelve un tipo primitivo como
int, ya que el lenguaje le impide asignar a unorvaluede ese tipo; estoy hablando del caso más general que incluye la devolución de tipos definidos por el usuario. )fuente
int.foo() = 42;es ilegal, independientemente de si el tipo de retorno se declaraintoint const. No creo que Scott defendiera el uso deconsten lugares como tipos de retorno y parámetros en declaraciones de funciones, donde el compilador lo ignora.Para tipos primitivos (como
int), la consistencia del resultado no importa. Para las clases, podría cambiar el comportamiento. Por ejemplo, es posible que no pueda llamar a un método que no sea constante en el resultado de su función:class Bigint { const C foo() const { ... } ... } Bigint b; b.foo().bar();Lo anterior está prohibido si
bar()es una función de miembro constante deC. En general, elija lo que tenga sentido.fuente
Mira eso:
const int operator[](const int index) constel
constfinal de la declaración. Describe que este método se puede llamar en constantes.En la otra mano cuando escribes solo
int& operator[](const int index)solo se puede llamar en instancias no constantes y también proporciona:
big_int[0] = 10;sintaxis.
fuente
Una de sus sobrecargas es devolver una referencia a un elemento en la matriz, que luego puede cambiar.
int& operator[](const int index) { return _data[index]; }La otra sobrecarga devuelve un valor para que lo use.
const int operator[](const int index) const { return _data[index]; }Dado que llama a cada una de estas sobrecargas de la misma manera, una nunca cambiará el valor cuando se use.
int foo = myBigInt[1]; // Doesn't change values inside the object. myBigInt[1] = 2; // Assigns a new value at index `fuente
En el ejemplo del operador de indexación de matrices (
operator[]), hace una diferencia.Con
int& operator[](const int index) { /* ... */ }puede usar la indexación para cambiar directamente la entrada en la matriz, por ejemplo, usándola así:
mybigint[3] = 5;El segundo, el
const int operator[](const int)operador se utiliza para obtener el valor únicamente.Sin embargo, como valor de retorno de funciones, para tipos simples como, por ejemplo
int, no importa. Lo que importa es si está devolviendo tipos más complejos, digamos astd::vector, y no desea que la persona que llama a la función modifique el vector.fuente
El
consttipo de devolución no es tan importante aquí. Desde temporales int no son modificables, no hay ninguna diferencia observable en el usointvsconst int. Vería una diferencia si utilizara un objeto más complejo que pudiera modificarse.fuente
Hay algunas buenas respuestas que giran en torno al tecnicismo de las dos versiones. Para un valor primitivo, no hace ninguna diferencia.
Sin embargo , siempre he considerado
constestar ahí para el programador más que para el compilador. Cuando escribeconst, está diciendo explícitamente "esto no debería cambiar". Seamos sinceros,constgeneralmente se puede eludir de todos modos, ¿verdad?Cuando devuelve un
const, le está diciendo al programador que usa esa función que el valor no debería cambiar. Y si lo está cambiando, probablemente esté haciendo algo mal, porque no debería tener que hacerlo.EDITAR: También creo que "usar
constsiempre que sea posible" es un mal consejo. Debería usarlo donde tenga sentido.fuente