c++ - operator - csharp+=
¿Por qué operator++ devuelve un valor no constante? (3)
Estoy bastante seguro de que esto se debe a que causaría estragos en las referencias de valores y cualquier tipo de tipo de decltype
. A pesar de que estas características no estaban en C ++ 03, se sabe que están llegando.
Más importante aún, no creo que ninguna función Estándar devuelva valores constantes, probablemente sea algo que no se consideró hasta después de que se publicó el Estándar. Además, generalmente no se considera que los valores constantes sean la cosa correcta para hacer ™. No todos los usos de las funciones de los miembros que no son constantes son inválidos, y los valores constantes de retorno los evitan de manera general.
Por ejemplo,
auto it = ++vec.begin();
es perfectamente válida, y de hecho, una semántica válida, si no es exactamente deseable. Considere mi clase que ofrece cadenas de método.
class ILikeMethodChains {
public:
int i;
ILikeMethodChains& SetSomeInt(int param) {
i = param;
return *this;
}
};
ILikeMethodChains func() { ... }
ILikeMethodChains var = func().SetSomeInt(1);
¿Debería ser rechazado solo porque quizás, a veces, podríamos llamar una función que no tiene sentido? No claro que no. ¿O qué tal "swaptimization"?
std::string func() { return "Hello World!"; }
std::string s;
func().swap(s);
Esto sería ilegal si func()
produjera una expresión constante, pero es perfectamente válida y, de hecho, suponiendo que la implementación de std::string
no asigne ninguna memoria en el constructor predeterminado, tanto rápida como legible / legible.
Lo que debe tener en cuenta es que las reglas de C ++ 03 rvalue / lvalue francamente no tienen sentido. Son, efectivamente, solo parcialmente horneados, y el mínimo requerido para rechazar algunos errores flagrantes mientras permite algunos derechos posibles. Las reglas de valor de C ++ 0x son mucho más sensatas y mucho más completas .
He leído Effective C ++ 3rd Edition escrito por Scott Meyers.
El artículo 3 del libro, "Usar const
siempre que sea posible", dice que si queremos evitar que los valores se asignen accidentalmente al valor de retorno de la función, el tipo de retorno debe ser const
.
Por ejemplo, la función de incremento para iterator
:
const iterator iterator::operator++(int) {
...
}
Entonces, se previenen algunos accidentes.
iterator it;
// error in the following, same as primitive pointer
// I wanted to compare iterators
if (it++ = iterator()) {
...
}
Sin embargo, los iteradores como std::vector::iterator
en GCC no devuelven valores const
.
vector<int> v;
v.begin()++ = v.begin(); // pass compiler check
¿Hay alguna razón para esto?
Si it
es constante, espero que *(++it)
me dé acceso mutable a lo que representa.
Sin embargo, al anular la referencia a un constitador solo se obtiene acceso no mutable a lo que representa. [ editar: no, esto también es incorrecto . ¡Realmente me rindo ahora!]
Esta es la única razón por la que puedo pensar.
Como señala con razón, lo siguiente está mal formado porque ++
en una primitiva produce un valor de r (que no puede estar en el LHS):
int* p = 0;
(p++)++;
Entonces, parece que hay algo de una inconsistencia en el lenguaje aquí.
EDIT : Esto no es realmente responder a la pregunta como se indica en los comentarios. Dejaré la publicación aquí en caso de que sea útil de todos modos.
Creo que esto es más o menos una cuestión de unificación de sintaxis hacia una mejor interfaz utilizable. Al proporcionar dichas funciones miembro sin diferenciar el nombre y permitir que solo el mecanismo de resolución de sobrecarga determine la versión correcta, se evita (o al menos se intenta) que el programador haga preocupaciones const
.
Sé que esto puede parecer contradictorio, en particular dado tu ejemplo. Pero si piensas en la mayoría de los casos de uso, tiene sentido. Tome un algoritmo STL como std::equal
. No importa si su contenedor es constante o no, siempre puede codificar algo como bool e = std::equal(c.begin(), c.end(), c2.begin())
sin tener que pensar en la versión correcta de principio y fin.
Este es el enfoque general en el STL. Recuerde al operator[]
... Teniendo en cuenta que los contenedores deben usarse con los algoritmos, esto es plausible. Aunque también se nota que en algunos casos es posible que aún deba definir un iterador con una versión que coincida ( iterator
o const_iterator
).
Bueno, esto es lo que me viene a la mente en este momento. No estoy seguro de lo convincente que es ...
Nota al const_iterator
: la forma correcta de usar iteradores constantes es a través de const_iterator
typedef.