Bei der zweiten Methode ist sichergestellt, dass die zurückgegebene
Liste nicht verändert werden kann. Aber die Variablen, auf die die
Pointer zeigen können nach wie vor verändert werden. Ich würde nun gerne
sicherstellen, dass wenn die Instanz von example const ist, auch über
getList() die Variablen, deren Adressen in der Liste stehen, nicht
geändert werden können. Die Methode soll also so aussehen:
1
conststd::vector<constint*>&getList()const;
Dazu brauche ich aber nun in der Implementierung einen cast. Ich habe
folgendes probiert:
Instanzen von einer Klasse mit unterschiedlichen Template Parametern
sind für den Compiler zwei völlig unterschiedliche Klassen. Dein
vector<int*> und vector<const int*> sind für den Compiler also genauso
verschieden wie string und vector<int>. Irgendwelche Casts zwischen
diesen Typen sind imho nicht möglich. Höchstens mit Gewalt und
reinterpret_cast, aber das würde ich nicht empfehlen. Eigentlich bleibt
dann nur eine Kopie des vectors anzulegen.
Da D. schrieb:> Ich würde nun gerne> sicherstellen, dass wenn die Instanz von example const ist, auch über> getList() die Variablen, deren Adressen in der Liste stehen, nicht> geändert werden können.
Darüber stolpert wohl jeder nach einer gewissen Zeit mit C++.
Sebastian V. schrieb:> Instanzen von einer Klasse mit unterschiedlichen Template Parametern> sind für den Compiler zwei völlig unterschiedliche Klassen.
Das mag aus Compilersicht so sein, es ist aber alles andere als
"logisch" aus C++ (Anwender) Sicht. Daher ist es auch in der "Defective
C++" Liste gelandet:
"For example, if your function accepts const std::vector<const char*>&
(which is supposed to mean "a reference to an immutable vector of
pointers to immutable built-in strings"), and I have a
std::vector<char*> object ("a mutable vector of mutable built-in
strings"), then I can't pass it to your function because the types
aren't convertible. You have to admit that it doesn't make any sense,
because your function guarantees that it won't change anything, and I
guarantee that I don't even mind having anything changed, and still the
C++ type system gets in the way and the only sane workaround is to copy
the vector."
http://yosefk.com/c++fqa/defective.html#defect-7
Das ist doch totaler Unsinn, die Vektoren sind Sequenzcontainer deren
Referenz kann man nicht so umcasten, so dass sich der Inhaltstyp ändert.
Wenn Du einen Zeiger auf eine Struktur bekommst in der der Anfang einer
verketteten Liste ist, dann kannst Du nicht durch rumbasteln an der
Struktuzr die Elementtypen der Liste beeinflussen.
Das ist auch keine Schwäche oder ein "Defekt" von C++. Der passende
C-Code wäre ein struct:
1
structlistentyp
2
{
3
listentyp*pNext;
4
listentyp*pPrevious;
5
anderertyp*pszData;
6
}
Wenn ich nun ein listentyp var1 bekomme dann kann ich nicht durch
umcasten von var1 in etwas anderes auf einmal alle pszData in der
gesamten Liste ändern.
Chris F. schrieb:> Das ist doch totaler Unsinn...
"totaler Unsinn" Tatsächlich? ;-)
Was ist mit const correctness?
Chris F. schrieb:> der passende C-Code...
C++ ist aber nicht C. In C++ möchte und kann man gerade die
Implementierungsdetails (effizient) verbergen.
@TS: Ein gängiger Workaround ist es selbst eine "Range" statt des
Containers zurück zu geben. Eine fixe Range ist alles was über bleibt,
wenn der Container und seine Elemente nicht modifzierbar sind (wäre
schön wenn es die Range einmal in die Sprache/STL schaffen würde.) Beim
Vector also bspw. einen Zeiger auf das erste Element (int const*) und
die Anzahl der Elemente (size_t). Grundsätzlich (STL Container) würde
man das wohl über Iteratoren lösen.
Mikro 7. schrieb:> Element (int const*) und die Anzahl der Elemente (size_t). Grundsätzlich> (STL Container) würde man das wohl über Iteratoren lösen.
Würde ich auch auf dem Weg lösen. Ein weiterer Punkt ist, dass man ein
const einfacher weg casten kann, als hinzu casten.
Auf C casts würde ich gänzlich verzichten.
Scott Meyers hat mit Effective C++ dem Thema const ein Kapitel gewidmet.
Das kann ich nur empfehlen.
Grüsse,
René
Das Thema hat eigentlich erstmal nix mit Templates oder Containern zu
tun. Auch mit einem Zeiger statt einem Vektor hat man das Problem.
Hättest du also einen Zeiger auf einen int und willst nun die Adresse
dieses Zeiger in einer Form, dass du darüber den int nicht ändern
kannst, hast du das gleiche Problem:
Rolf M. schrieb:> Das Thema hat eigentlich erstmal nix mit Templates oder Containern> zu> tun. Auch mit einem Zeiger statt einem Vektor hat man das Problem.> Hättest du also einen Zeiger auf einen int und willst nun die Adresse> dieses Zeiger in einer Form, dass du darüber den int nicht ändern> kannst, hast du das gleiche Problem: int* p;> const int ** q = &p; // Fehler: Inkompatible Typen
Natürlich geht das, man muss lediglich einen const cast verwenden. Und
es hat sehr wohl mit Template zu tun. Sorry Rolf.
Beispiel:
Es bleibt dabei, man kann nicht durch Typumwandlung eines Containers die
Inhaltstypen ändern. Das ist hier der Fehler.
Der einzige Beitrag der zu einer Lösung des Problems vom TE führt ist
der Hinweis von Sebastian mit der Kopie. Der Konstruktor von vector kann
mit einer Range begin->end befüllt werden.
Die Person, die Lösungen resp. Posts (von Chris F. und mir) mit minus
Bewertungen bewertet, darf gerne ohne sich zu outen, eine Lösung
formulieren. Wir sind alle sehr gespannt!
Grüsse,
René
PS: die Mühe mache ich mir hier nicht mehr, zu posten.
Hallo Da,
Da D. schrieb:> Hat jemand einen Tipp, wie man solch einen cast hinbekommen kann?
Konform wirst Du den cast nicht hin bekommen, da es wirklich zwei
unterschiedliche Typen sind und (recht akademisch) die Implementierungen
auch total unterschiedlich sein könnten. Ein static_cast/const_cast über
void* würde sicher funktionieren, aber das muss nicht funktionieren.
Eine nicht konstante Referenz auf einen member zurück zugeben ist eh
recht "sinnfrei", dann kannst Du den member auch gleich public machen.
Wenn es Dir aber nicht darum geht, dass ein Nutzer Elemente aus list
entfernen oder hinzufügen kann, sondern nur Operationen auf Elementen
aus list verwenden kann, die den Zustand der Elemente auch ändern
können, würden sich Iteratoren als Interface anbieten:
Rene H. schrieb:> Rolf M. schrieb:>> Das Thema hat eigentlich erstmal nix mit Templates oder Containern>> zu>> tun. Auch mit einem Zeiger statt einem Vektor hat man das Problem.>> Hättest du also einen Zeiger auf einen int und willst nun die Adresse>> dieses Zeiger in einer Form, dass du darüber den int nicht ändern>> kannst, hast du das gleiche Problem: int* p;>> const int ** q = &p; // Fehler: Inkompatible Typen>> Natürlich geht das, man muss lediglich einen const cast verwenden.
Das erstaunt mich doch sehr. Eigentlich kenne ich const_cast nur als
Mittel, um const wegzucasten. Dass man es auf zweiter Pointer-Ebene auch
benutzen könnte, um ein const dazuzucasten, darauf wäre ich im Leben
nicht gekommen.
Du könntest statt int* einen Typ in dem vector speichern, der sich wie
ein Pointer verhält, aber die gewünschte constness beibehält:
http://en.cppreference.com/w/cpp/experimental/propagate_const
Ist zwar noch nicht Standard, aber kannst Du ja auch selber
implementieren.
Rolf M. schrieb:> Hättest du also einen Zeiger auf einen int und willst nun die Adresse> dieses Zeiger in einer Form, dass du darüber den int nicht ändern> kannst, hast du das gleiche Problem:> int* p;> const int ** q = &p; // Fehler: Inkompatible Typen
Die Zuweisung ist falsch! [http://c-faq.com/ansi/constmismatch.html]
Korrekt ist:
Mikro 7. schrieb:> int const * const *q = &p ;Rene H. schrieb:> const int ** q = const_cast<const int**>(&p);Rolf M. schrieb:> Das erstaunt mich doch sehr. Eigentlich kenne ich const_cast nur als> Mittel, um const wegzucasten.
Macht es jetzt Sinn!?
Mikro 7. schrieb:> Rolf M. schrieb:>> Hättest du also einen Zeiger auf einen int und willst nun die Adresse>> dieses Zeiger in einer Form, dass du darüber den int nicht ändern>> kannst, hast du das gleiche Problem:>> int* p;>> const int ** q = &p; // Fehler: Inkompatible Typen>> Die Zuweisung ist falsch! [http://c-faq.com/ansi/constmismatch.html]
Äh ja, genau deshalb hab ich sie doch hingeschrieben - um zu zeigen,
dass sie falsch ist.
> Korrekt ist:>> Mikro 7. schrieb:>> int const * const *q = &p ;>> Rene H. schrieb:>> const int ** q = const_cast<const int**>(&p);>> Rolf M. schrieb:>> Das erstaunt mich doch sehr. Eigentlich kenne ich const_cast nur als>> Mittel, um const wegzucasten.>> Macht es jetzt Sinn!?
Auch das ist genau wie ich geschrieben habe: Ich bin erstaunt, da ich
dachte, const_cast sei nur zum wegcasten von const da und nicht zum
hinzucasten. Auch aus dem Stroustrup geht das nicht so eindeutig
hervor.
Dass das von "Mikro" funktioniert, erstaunt mich noch mehr. Warum macht
das zusätzliche const beim int jetzt nichts mehr aus, wenn man auch noch
den Zeiger const macht? Die sollten doch eigentlich nichts mit einander
zu tun haben. Die Regel, nach der ich dachte, dass es funktioniert, gibt
es wohl so nicht. Mir fehlt jetzt eine Regel, die stattdessen gilt.
Rolf M. schrieb:> Mikro 7. schrieb:>> Die Zuweisung ist falsch! [http://c-faq.com/ansi/constmismatch.html]>> Äh ja, genau deshalb hab ich sie doch hingeschrieben - um zu zeigen,> dass sie falsch ist.
Wenn du das "weißt", weißt du denn auch, dass sie nicht zum
Eingangsposting paßt? Du würdest versuchen einen "std::vector<int
const>&" zurück zu liefern statt "std::vector<int const> const&"!
Das hier ist die äquivalente Situation zum Eingangsposting:
>> Mikro 7. schrieb:>>> int const * const *q = &p ;> Auch das ist genau wie ich geschrieben habe: Ich bin erstaunt...
Nö. Vielleicht (nochmal?) in Ruhe die Postings hier und die FAQ lesen?!