2016-03-24 16 views
0

Ich habe eine Funktion, die nach einem Element in einer benutzerdefinierten Listenimplementierung sucht. Um es const-correct zu machen, kann ich diese ordentliche Überladung machen und sogar einen Einzeiler verwenden, um die gleiche Implementierung wiederzuverwenden, so dass Logik nicht dupliziert werden muss.Wie const_cast ein Vektor von const Zeigern zu einem Vektor von nicht-const Zeigern?

const MyObject* findObject(const MyList& list) 
{ 
    //... 
} 

MyObject* findObject(MyList& list) 
{ 
    return const_cast<MyObject*>(findObject(const_cast<const MyList&>(list))); 
} 

Das Problem ist, was soll ich tun, wenn ich mehr Elemente Zeiger innerhalb eines Vektors Sperre unsave/nicht-portable Hacks wie reinterpret_cast zurückkehren muß?

+0

Was ist der Punkt von 'const_cast (list)' – SergeyA

+0

Der 'const'-Bezeichner für Argumente ist eine Garantie für den Aufrufer, dass die Funktion die übergebenen Daten nicht ändert. Es ist nicht erforderlich, dass die Daten übergeben werden. Sie können nicht konstante Daten übergeben. Also ist deine 'const_cast (list)' sinnlos. Auch die Rückgabe von 'const MyObject *' aus 'findObject()' ist sinnlos. Um zu sehen, warum, überlege dir diesen Ausschnitt: 'const int C = 42; int i = C; ' –

+0

schreiben Sie es richtig und leise gehen weg von diesen kindischen const_cast Ideen. Sie wissen, warum sie Sicherheitsschüsse auf Waffen setzen, oder? –

Antwort

2

Die einfachste Lösung ist, const_cast zu vergessen und nur beide Überladungen explizit zu implementieren. Dies ist, was Standard-Bibliothek-Implementierungen tun.

Zum Beispiel hat std::vector eine const und nicht const Version von operator[]. Sowohl VC++ als auch GCC haben doppelte Implementierungen für diesen Operator (siehe include/vector oder stl_vector.h Dateien); Weder greift man auf const_cast Tricks zurück, um das Verhalten zu duplizieren.

Nun, wenn Ihre findObject Implementierung ist sehr groß und kompliziert, dann sollte die erste Wahl sein, es einfacher zu machen. Als vorübergehende Problemumgehung können Sie beide Überladungen im Hinblick auf eine interne (private oder anderweitig nicht zugängliche) Vorlagenfunktion implementieren, indem Sie den Rückgabetyp decltype verwenden, um über das Argument den richtigen const oder nicht-const Rückgabetyp abzurufen. Hier ist ein einfaches Beispiel:

#include <iostream> 
#include <vector> 
#include <typeinfo> // just to demonstrate what's going on 

// simple example data structure: 
struct MyObject {}; 
struct MyList 
{ 
    MyObject elements[3] = { MyObject {}, MyObject {}, MyObject {} }; 
}; 

namespace internal 
{ 
    // &list.elements[0] will be either MyObject const* or MyObject* 
    template <class ConstOrNonConstList> 
    auto doFindObject(ConstOrNonConstList& list) -> std::vector<decltype(&list.elements[0])> 
    { 
     // let's say there was an immensely complicated function here, 
     // with a lot of error potential and maintenance nightmares 
     // lurking behind a simple copy & paste solution... 

     // just to demonstrate what's going: 
     std::cout << typeid(decltype(&list.elements[0])).name() << "\n"; 

     std::vector<decltype(&list.elements[0])> result; 
     for (auto&& element : list.elements) 
     { 
      result.push_back(&element); 
     } 
     return result; 
    } 
} 

std::vector<const MyObject*> findObject(const MyList& list) 
{ 
    std::cout << "const version\n"; 
    return internal::doFindObject(list); 
} 

std::vector<MyObject*> findObject(MyList& list) 
{ 
    std::cout << "non-const version\n"; 
    return internal::doFindObject(list); 
} 

int main() 
{ 
    MyList list1; 
    MyList const list2; 
    auto obj1 = findObject(list1); 
    auto obj2 = findObject(list2); 
} 

Beispiel Ausgang (je nachdem, welche Art von Namen typeid produziert von den jeweiligen Implementierung):

non-const version 
struct MyObject * 
const version 
struct MyObject const * 

Aber ehrlich gesagt, ich würde das nicht tun. Es scheint überentwickelt, und es ist vielleicht ein bisschen zu schlau. Übermäßig cleverer Code ist selten eine gute Idee, weil er Leute verwirrt.

+0

Diese Technik ist von Scott Meyers Effective C++ entlehnt. Wenn Bibliotheksdesigner es vermeiden, was sind dann seine Schattenseiten? Ich kann wirklich bis jetzt nicht sehen. Es ist auch nicht möglich/ratsam, eine ganze Methode immer zu überarbeiten, auch wenn sie zu groß ist. Betrachten Sie zum Beispiel den Umgang mit getestetem Legacy-Code. Und trotzdem, selbst wenn es am Ende nur Grenzen und andere Grundlagen gibt, bevorzuge ich es einmal und zweimal verwendet zu haben, anstatt es an zwei Stellen zu duplizieren und auf den Entwickler zu setzen, um beide zu synchronisieren, wenn er Änderungen vornimmt (praktische Erfahrung) zeigt: er wird häufig nicht) – user1709708