Gibt es Fälle, in denen const_cast funktioniert, eine private const-Funktion, die nicht-const zurückgibt, jedoch nicht, um nicht-triviale C ++ const-bezogene Duplikationen zu vermeiden?
In Scott Meyers ' Effective C ++, Punkt 3, schlägt er vor, dass ein const_cast in Kombination mit einem statischen Cast eine effektive und sichere Methode sein kann, um doppelten Code zu vermeiden, z
const void* Bar::bar(int i) const
{
...
return variableResultingFromNonTrivialDotDotDotCode;
}
void* Bar::bar(int i)
{
return const_cast<void*>(static_cast<const Bar*>(this)->bar(i));
}
Meyers erklärt weiter, dass es gefährlich ist, wenn die const-Funktion die Nicht-const-Funktion aufruft.
Der folgende Code ist ein Gegenbeispiel:
- Entgegen Meyers Vorschlag ist der const_cast in Kombination mit einem statischen Cast manchmal gefährlich
- Manchmal ist es weniger gefährlich, wenn die const-Funktion den non-const aufruft
- manchmal verbergen beide Methoden, die einen const_cast verwenden, potenziell nützliche Compiler-Fehler
- Das Vermeiden eines const_cast und das Zurückgeben eines Nicht-const durch ein zusätzliches privates const-Mitglied ist eine weitere Option
Gilt eine der Strategien von const_cast zur Vermeidung von Code-Duplikationen als bewährte Methode? Möchten Sie stattdessen die Strategie der privaten Methode bevorzugen? Gibt es Fälle, in denen const_cast funktionieren würde, eine private Methode jedoch nicht? Gibt es andere Möglichkeiten (außer der Vervielfältigung)?
Mein Anliegen bei den const_cast-Strategien ist, dass selbst wenn der Code beim Schreiben korrekt ist, der Code später während der Wartung inkorrekt werden könnte und der const_cast einen nützlichen Compilerfehler verbergen würde. Es scheint, als ob eine gemeinsame private Funktion im Allgemeinen sicherer ist.
class Foo
{
public:
Foo(const LongLived& constLongLived, LongLived& mutableLongLived)
: mConstLongLived(constLongLived), mMutableLongLived(mutableLongLived)
{}
// case A: we shouldn't ever be allowed to return a non-const reference to something we only have a const reference to
// const_cast prevents a useful compiler error
const LongLived& GetA1() const { return mConstLongLived; }
LongLived& GetA1()
{
return const_cast<LongLived&>( static_cast<const Foo*>(this)->GetA1() );
}
/* gives useful compiler error
LongLived& GetA2()
{
return mConstLongLived; // error: invalid initialization of reference of type 'LongLived&' from expression of type 'const LongLived'
}
const LongLived& GetA2() const { return const_cast<Foo*>(this)->GetA2(); }
*/
// case B: imagine we are using the convention that const means thread-safe, and we would prefer to re-calculate than lock the cache, then GetB0 might be correct:
int GetB0(int i) { return mCache.Nth(i); }
int GetB0(int i) const { return Fibonachi().Nth(i); }
/* gives useful compiler error
int GetB1(int i) const { return mCache.Nth(i); } // error: passing 'const Fibonachi' as 'this' argument of 'int Fibonachi::Nth(int)' discards qualifiers
int GetB1(int i)
{
return static_cast<const Foo*>(this)->GetB1(i);
}*/
// const_cast prevents a useful compiler error
int GetB2(int i) { return mCache.Nth(i); }
int GetB2(int i) const { return const_cast<Foo*>(this)->GetB2(i); }
// case C: calling a private const member that returns non-const seems like generally the way to go
LongLived& GetC1() { return GetC1Private(); }
const LongLived& GetC1() const { return GetC1Private(); }
private:
LongLived& GetC1Private() const { /* pretend a bunch of lines of code instead of just returning a single variable*/ return mMutableLongLived; }
const LongLived& mConstLongLived;
LongLived& mMutableLongLived;
Fibonachi mCache;
};
class Fibonachi
{
public:
Fibonachi()
{
mCache.push_back(0);
mCache.push_back(1);
}
int Nth(int n)
{
for (int i=mCache.size(); i <= n; ++i)
{
mCache.push_back(mCache[i-1] + mCache[i-2]);
}
return mCache[n];
}
int Nth(int n) const
{
return n < mCache.size() ? mCache[n] : -1;
}
private:
std::vector<int> mCache;
};
class LongLived {};
Antworten:
Bei der Implementierung von konstanten und nicht konstanten Memberfunktionen, die sich nur dadurch unterscheiden, ob es sich bei dem zurückgegebenen ptr / reference um const handelt, ist die beste DRY-Strategie:
z.B
Nennen wir dies die private const-Funktion, die ein Muster zurückgibt, das nicht const ist .
Dies ist die beste Strategie, um Duplikationen auf einfache Weise zu vermeiden und dem Compiler dennoch die Möglichkeit zu geben, potenziell nützliche Überprüfungen durchzuführen und const-bezogene Fehlermeldungen zu melden.
quelle
const
Instanz erhalten können (es sei denn, der Verweis bezieht sich auf etwas Deklariertesmutable
oder Sie verwenden ein,const_cast
aber in beiden Fällen gibt es zunächst kein Problem ). Auch konnte ich nichts auf der "private const-Funktion zurückgeben Nicht-const-Muster" (wenn es ein Witz sein sollte, um es Muster zu nennen ... es ist nicht lustig;)Ja, Sie haben Recht: Viele C ++ - Programme, die versuchen, die Konstanten zu korrigieren, verstoßen stark gegen das DRY-Prinzip, und selbst das private Mitglied, das die Konstanten nicht zurückgibt, ist ein bisschen zu komplex für den Komfort.
Sie übersehen jedoch eine Beobachtung: Eine Codeduplizierung aufgrund von Konstanten-Korrektheit ist immer nur dann ein Problem, wenn Sie anderen Codezugriff auf Ihre Datenelemente gewähren. Dies an sich verstößt gegen die Kapselung. Im Allgemeinen tritt diese Art der Code-Vervielfältigung meistens bei einfachen Zugriffsmethoden auf (schließlich geben Sie den Zugriff an bereits vorhandene Mitglieder weiter, der Rückgabewert ist im Allgemeinen nicht das Ergebnis einer Berechnung).
Ich habe die Erfahrung gemacht, dass gute Abstraktionen nicht dazu neigen, Accessoren einzuschließen. Infolgedessen vermeide ich dieses Problem weitgehend, indem ich Elementfunktionen definiere, die tatsächlich etwas tun, anstatt nur den Zugriff auf Datenelemente zu ermöglichen. Ich versuche, Verhalten statt Daten zu modellieren. Meine Hauptabsicht dabei ist es, eine gewisse Abstraktion sowohl meiner Klassen als auch ihrer einzelnen Elementfunktionen zu erzielen, anstatt nur meine Objekte als Datencontainer zu verwenden. Aber dieser Stil ist auch recht erfolgreich, um die Unmengen von sich wiederholenden Einzeilen-Zugriffsmethoden (const / non-const) zu vermeiden, die in den meisten Codes so häufig vorkommen.
quelle