43 Stimmen

Vereinfachung der Konst-Überladung?

Ich unterrichte seit vielen Jahren einen C++-Programmierkurs, und eines der schwierigsten Dinge, die man den Studenten erklären muss, ist das Überladen von Konstanten. Ich verwende häufig das Beispiel einer vektorartigen Klasse und ihrer operator[] Funktion:

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

Ich habe wenig bis keine Schwierigkeiten zu erklären, warum zwei Versionen der operator[] Funktion benötigt werden, aber bei dem Versuch, zu erklären, wie man die beiden Implementierungen zusammenführt, verliere ich oft viel Zeit mit Sprach-Arkanen. Das Problem ist, dass die einzige gute und zuverlässige Methode, die ich kenne, um eine dieser Funktionen in Bezug auf die andere zu implementieren, die const_cast / static_cast Trick:

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]);
}

Das Problem bei dieser Vorgehensweise ist, dass sie äußerst kompliziert zu erklären und keineswegs intuitiv einleuchtend ist. Wenn man es als "cast to const, dann call the const version, dann strip off constness" erklärt, ist es ein wenig leichter zu verstehen, aber die eigentliche Syntax ist erschreckend,. Erläutern, was const_cast ist, warum er hier angebracht ist und warum er anderswo fast durchgängig unangebracht ist, kostet mich normalerweise fünf bis zehn Minuten Vortragszeit, und der Sinn dieses ganzen Ausdrucks zu verstehen, erfordert oft mehr Mühe als der Unterschied zwischen const T* y T* const . Ich bin der Meinung, dass die Studierenden wissen müssen, was const-overloading ist und wie man es macht, ohne den Code in den beiden Funktionen unnötig zu duplizieren, aber dieser Trick scheint in einem Einführungskurs in die C++-Programmierung etwas übertrieben.

Meine Frage lautet: Gibt es einen einfacheren Weg zur Umsetzung const -überlasteten Funktionen im Verhältnis zueinander? Oder gibt es eine einfachere Möglichkeit, den Schülern diesen bestehenden Trick zu erklären?

12voto

Ich betrachte dies in der Regel als eine sprachliche Einschränkung und rate den Leuten, dass sie - sofern sie nicht wirklich wissen, was sie tun - sie sollten es einfach neu implementieren. In den meisten Fällen handelt es sich bei diesen Funktionen um einfache einzeilige Getter, so dass es keine Probleme gibt.

In Ihrer Eigenschaft als C++-Lehrer würde ich diesen Ansatz sogar noch stärker befürworten.

8voto

Sean Punkte 28106

Wie wäre es, wenn Sie es einfach in kleinere Schritte unterteilen?

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

Sie können sogar die static_cast auf diese Weise, obwohl Sie ihn auch weglassen könnten, wenn Sie meinen, dass es klarer wäre.

7voto

Roman L Punkte 2976

Es ist eine ziemlich seltsame Option, aber dies kann mit einem statischen Template-Helfer wie diesem gemacht werden

// 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] */ }

Dann können Sie schreiben

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

Dieser Trick vermeidet Würfe (!) und doppelte Implementierung, aber auch hier sieht es für mich seltsam aus :)

Und noch eine kleine Bemerkung zu Ihrem Codeschnipsel, der nicht const_cast stattdessen genug sein static_cast oder übersehe ich etwas?

6voto

Martin York Punkte 245363

Sie können eine Besetzung mit Hilfe einer privaten Methode aufheben:
Sie fügt eine Methode hinzu, macht das Gießen aber weniger komplex:

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 */;}
};

4voto

Edward Strange Punkte 39597

Meiner Meinung nach ist das einfach nur dumm. Sie erzwingen, dass das eine in Form des anderen implementiert wird, nur weil Sie es wollen, und nicht, weil der resultierende Code leichter zu pflegen oder zu verstehen ist. Der Grund, warum Ihre Schüler verwirrt sind, ist wahrscheinlich, dass sie es sein SOLLTEN.

Nicht jeder Grundsatz sollte bis zum äußersten getrieben werden. Manchmal ist es einfach besser, redundant zu sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X