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.
Was ist der Punkt von 'const_cast (list)' –
SergeyA
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; ' –
schreiben Sie es richtig und leise gehen weg von diesen kindischen const_cast Ideen. Sie wissen, warum sie Sicherheitsschüsse auf Waffen setzen, oder? –