c++ const overloading

c++ - Simplificando la sobrecarga de const?



overloading (7)

¿Qué tal simplemente descomponerlo en pasos más pequeños?

const Vector<T>& const_this = *this; const T& const_elem = const_this[index]; T& mutable_elem = const_cast<T&>(const_elem); return mutable_elem;

Incluso puedes eliminar el static_cast esta manera, aunque puedes dejarlo si crees que sería más claro.

He estado enseñando una clase de programación en C ++ durante muchos años y una de las cosas más difíciles de explicar a los estudiantes es la sobrecarga de const. Normalmente uso el ejemplo de una clase similar a un vector y su función de operator[] :

template <typename T> class Vector { public: T& operator[] (size_t index); const T& operator[] (size_t index) const; };

No tengo ningún problema para explicar por qué es que se necesitan dos versiones de la función operator[] , pero al tratar de explicar cómo unificar las dos implementaciones, a menudo pierdo mucho tiempo con los arcanos de lenguaje. El problema es que la única manera buena y confiable de saber cómo implementar una de estas funciones en términos de la otra es con el truco const_cast / static_cast :

template <typename T> const T& Vector<T>::operator[] (size_t index) const { /* ... your implementation here ... */ } template <typename T> T& Vector<T>::operator[] (size_t index) { return const_cast<T&>(static_cast<const Vector&>(*this)[index]); }

El problema con esta configuración es que es extremadamente difícil de explicar y nada intuitivamente obvio. Cuando lo explica como "cast to const, luego llame a la versión const, luego elimine la constness" es un poco más fácil de entender, pero la sintaxis real es aterradora. Explicar qué es const_cast , por qué es apropiado aquí, y por qué es casi universalmente inadecuado en otros lugares por lo general me lleva de cinco a diez minutos de clase, y dar sentido a toda esta expresión a menudo requiere más esfuerzo que la diferencia entre const T* y T* const . Siento que los estudiantes necesitan saber sobre la sobrecarga de const y cómo hacerlo sin duplicar innecesariamente el código en las dos funciones, pero este truco parece un poco excesivo en un curso de programación de introducción a C ++.

Mi pregunta es la siguiente: ¿existe una forma más sencilla de implementar funciones sobrecargadas de const en términos de la otra? ¿O hay una forma más sencilla de explicar este truco existente a los estudiantes?


En lugar de hacer que una versión llame a la otra, puede permitir que ambos llamen a una función auxiliar que encuentre el elemento correcto. Parece que ya estás introduciendo plantillas, por lo que permitir que la función auxiliar sea una plantilla también debería funcionar y evitar la duplicación de código, y trabajar tanto para const como para non-const sin ningún const_cast s.

Alternativamente, puede usar variables locales para ayudar a dividir la expresión en partes manejables (con comentarios para cada una).

const ClassType& cthis = *this; // look, no explicit cast needed here :) const T& elem = cthis[index]; // delegate to const version return const_cast<T&>(elem); // ok to strip off the const, since we added it in the first place


En mi opinión esto es una tontería. Está obligando a que uno se implemente en términos del otro simplemente por hacerlo, no porque el código resultante sea más fácil de mantener o entender. La razón por la que sus estudiantes están confundidos es probablemente porque DEBEN estarlo.

No todos los principios deben ser llevados al extremo exclusivo. A veces ser redundante es simplemente mejor.


Es una opción bastante extraña, pero esto se puede hacer usando un ayudante de plantilla estática como este

// template parameters can be const or non-const template<class Ret, class C> static Ret& get(C* p, size_t index) { /* common code here like p->array[index] */ }

Entonces puedes escribir

const T& operator[](size_t index) const { return get<const T>(this, index); } T& operator[](size_t index) { return get<T>(this, index); }

Este truco evita los lanzamientos (!) Y la implementación doble, pero, de nuevo, me parece extraño :)

Y un pequeño comentario sobre tu código de fragmento, ¿no será suficiente const_cast lugar de static_cast , o me falta algo?


Por lo general, considero que esto es una restricción de idioma, y ​​les aconsejo a las personas que, a menos que realmente sepan lo que están haciendo, deberían simplemente volver a implementar. En la gran mayoría de los casos, estas funciones son simples captadores de una línea, por lo que no hay dolor.

En su capacidad de enseñar C ++, me sentiría aún más firme con respecto a este enfoque.


Puedes eliminar un lanzamiento usando un método privado:
Agrega un método pero hace que el casting sea menos complejo:

template <typename T> class Vector { public: T const& operator[](size_t i) const { return getValue(i);} T& operator[](size_t i) { return const_cast<T&>(getValue(i));} private: T const& getValue(size_t i) const { return /* STUFF */;} };


Si la implementación es exactamente el mismo código (para este ejemplo de clase ''vectorial'' o lo que sea), entonces ¿por qué no hacer que la versión no constante llame a la versión constante, en lugar de hacerlo al revés? Si por alguna razón el código tiene que modificar un miembro, entonces tal vez no debería haber una versión constante (ignorando todo lo que se puede cambiar ...).