¿Cómo elimino la duplicación de código entre funciones miembro constantes y no constantes similares?

Resuelto Kevin asked hace 16 años • 21 respuestas

Digamos que tengo lo siguiente class Xdonde quiero devolver el acceso a un miembro interno:

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    Z& Z(size_t index)
    {
        // massive amounts of code for validating index

        Z& ret = vecZ[index];

        // even more code for determining that the Z instance
        // at index is *exactly* the right sort of Z (a process
        // which involves calculating leap years in which
        // religious holidays fall on Tuesdays for
        // the next thousand years or so)

        return ret;
    }
    const Z& Z(size_t index) const
    {
        // identical to non-const X::Z(), except printed in
        // a lighter shade of gray since
        // we're running low on toner by this point
    }
};

Las dos funciones miembro X::Z()tienen X::Z() constcódigo idéntico dentro de las llaves. Este es código duplicado y puede causar problemas de mantenimiento para funciones largas con lógica compleja .

¿Hay alguna manera de evitar la duplicación de este código?

Kevin avatar Sep 24 '08 03:09 Kevin
Aceptado

Para obtener una explicación detallada, consulte el título "Evitar la duplicación en funciones de miembros consty no constmiembros", en la pág. 23, en el elemento 3 "Utilizar constsiempre que sea posible", en Effective C++ , edición 3D de Scott Meyers, ISBN-13: 9780321334879.

texto alternativo

Aquí está la solución de Meyers (simplificada):

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

Las dos conversiones y la llamada a función pueden ser feas, pero son correctas en un constmétodo que no es un método, ya que eso implica que el objeto no era constpara empezar. (Meyers tiene una discusión exhaustiva sobre esto).

jwfearn avatar Sep 23 '2008 21:09 jwfearn

C++17 ha actualizado la mejor respuesta para esta pregunta:

T const & f() const {
    return something_complicated();
}
T & f() {
    return const_cast<T &>(std::as_const(*this).f());
}

Esto tiene las ventajas de que:

  • Es obvio lo que esta pasando
  • Tiene una sobrecarga de código mínima: cabe en una sola línea
  • Es difícil equivocarse (sólo se puede descartar volatilepor accidente, pero volatilees un calificador poco común)

Si desea seguir la ruta de deducción completa, puede lograrlo teniendo una función de ayuda.

template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
    return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
    return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
    return value;
}
template<typename T>
void as_mutable(T const &&) = delete;

Ahora ni siquiera puedes equivocarte volatiley el uso parece

decltype(auto) f() const {
    return something_complicated();
}
decltype(auto) f() {
    return as_mutable(std::as_const(*this).f());
}
David Stone avatar Nov 18 '2017 17:11 David Stone