Me sorprende que haya tantas respuestas diferentes, pero casi todas se basan en una gran plantilla de magia. Las plantillas son poderosas, pero a veces las macros las superan con concisión. La máxima versatilidad a menudo se logra combinando ambos.
Escribí una macro FROM_CONST_OVERLOAD()
que se puede colocar en la función no const para invocar la función const.
Ejemplo de uso:
class MyClass
{
private:
std::vector<std::string> data = {"str", "x"};
public:
// Works for references
const std::string& GetRef(std::size_t index) const
{
return data[index];
}
std::string& GetRef(std::size_t index)
{
return FROM_CONST_OVERLOAD( GetRef(index) );
}
// Works for pointers
const std::string* GetPtr(std::size_t index) const
{
return &data[index];
}
std::string* GetPtr(std::size_t index)
{
return FROM_CONST_OVERLOAD( GetPtr(index) );
}
};
Implementación simple y reutilizable:
template <typename T>
T& WithoutConst(const T& ref)
{
return const_cast<T&>(ref);
}
template <typename T>
T* WithoutConst(const T* ptr)
{
return const_cast<T*>(ptr);
}
template <typename T>
const T* WithConst(T* ptr)
{
return ptr;
}
#define FROM_CONST_OVERLOAD(FunctionCall) \
WithoutConst(WithConst(this)->FunctionCall)
Explicación:
Como se publica en muchas respuestas, el patrón típico para evitar la duplicación de código en una función miembro no constante es este:
return const_cast<Result&>( static_cast<const MyClass*>(this)->Method(args) );
Se puede evitar mucho de este repetitivo usando inferencia de tipos. Primero, const_cast
se puede encapsular en WithoutConst()
, lo que infiere el tipo de su argumento y elimina el calificador const. En segundo lugar, se puede utilizar un enfoque similar WithConst()
para calificar constantemente el this
puntero, lo que permite llamar al método const-overloaded.
El resto es una macro simple que antepone la llamada con la calificación correcta this->
y elimina const del resultado. Dado que la expresión utilizada en la macro es casi siempre una simple llamada a función con argumentos reenviados 1: 1, los inconvenientes de las macros, como la evaluación múltiple, no entran en acción. Los puntos suspensivos y __VA_ARGS__
también podrían usarse, pero no deberían ser necesarios porque las comas (como los separadores de argumentos) aparecen entre paréntesis.
Este enfoque tiene varios beneficios:
- Sintaxis mínima y natural: simplemente envuelva la llamada
FROM_CONST_OVERLOAD( )
- No se requiere función de miembro adicional
- Compatible con C ++ 98
- Implementación simple, sin metaprogramación de plantilla y cero dependencias
- Extensibles: const otras relaciones pueden ser añadidos (como
const_iterator
, std::shared_ptr<const T>
, etc.). Para esto, simplemente sobrecargue WithoutConst()
los tipos correspondientes.
Limitaciones: esta solución está optimizada para escenarios en los que la sobrecarga no constante está haciendo exactamente lo mismo que la sobrecarga constante, de modo que los argumentos pueden reenviarse 1: 1. Si su lógica es diferente y no está llamando a la versión constante a través de this->Method(args)
, puede considerar otros enfoques.