Ich versuche, ein gutes Design für meine Software zu definieren, die über Lese-/Schreibzugriff auf einige Variablen wobei darauf geachtet, impliziert. Hier habe ich das Programm für die Diskussion vereinfacht. Hoffentlich wird dies auch für andere hilfreich sein. :-)Shared_ptr <T> Shared_ptr <T const> und Vektor Vektor <T><T const>
Lassen Sie uns sagen, dass wir eine Klasse X wie folgt haben:
class X {
int x;
public:
X(int y) : x(y) { }
void print() const { std::cout << "X::" << x << std::endl; }
void foo() { ++x; }
};
Lasst uns auch sagen, dass diese Klasse in der Zukunft mit X1 wird subclassed, X2, ..., die print()
und foo()
reimplementieren können . (I entfallen die erforderlichen virtual
Schlüsselwörter der Einfachheit halber hier, da es nicht das eigentliche Problem, das ich bin vor.)
Da wir polymorphisme verwenden, lassen Sie uns verwenden (smart) Zeiger und definieren eine einfache Fabrik:
using XPtr = std::shared_ptr<X>;
using ConstXPtr = std::shared_ptr<X const>;
XPtr createX(int x) { return std::make_shared<X>(x); }
Bis jetzt ist alles in Ordnung: Ich kann goo(p)
definieren, die lesen und schreiben kann und hoo(p)
, die nur lesen können.
void goo(XPtr p) {
p->print();
p->foo();
p->print();
}
void hoo(ConstXPtr p) {
p->print();
// p->foo(); // ERROR :-)
}
Und der Anruf Seite sieht wie folgt aus:
XPtr p = createX(42);
goo(p);
hoo(p);
Der gemeinsame Zeiger auf X (XPtr
) automatisch in seine const Version umgewandelt wird (ConstXPtr
). Schön, genau das will ich!
Jetzt kommen die Probleme: Ich brauche eine heterogene Sammlung von X
. Meine Wahl ist eine std::vector<XPtr>
. (Es könnte auch ein list
sein, warum nicht.)
Das Design, das ich im Sinn habe, ist das Folgende. Ich habe zwei Versionen des Containers: einen mit Lese-/Schreibzugriff auf seine Elemente, einen mit Lesezugriff auf seine Elemente.
using XsPtr = std::vector<XPtr>;
using ConstXsPtr = std::vector<ConstXPtr>;
Ich habe eine Klasse bekam, der diese Daten verarbeitet:
class E {
XsPtr xs;
public:
E() {
for (auto i : { 2, 3, 5, 7, 11, 13 }) {
xs.emplace_back(createX(std::move(i)));
}
}
void loo() {
std::cout << "\n\nloo()" << std::endl;
ioo(toConst(xs));
joo(xs);
ioo(toConst(xs));
}
void moo() const {
std::cout << "\n\nmoo()" << std::endl;
ioo(toConst(xs));
joo(xs); // Should not be allowed
ioo(toConst(xs));
}
};
Die ioo()
und joo()
Funktionen sind wie folgt:
void ioo(ConstXsPtr xs) {
for (auto p : xs) {
p->print();
// p->foo(); // ERROR :-)
}
}
void joo(XsPtr xs) {
for (auto p: xs) {
p->foo();
}
}
Wie Sie sehen können, in E::loo()
und E::moo()
Ich muss etwas Umwandlung mit toConst()
:
ConstXsPtr toConst(XsPtr xs) {
ConstXsPtr cxs(xs.size());
std::copy(std::begin(xs), std::end(xs), std::begin(cxs));
return cxs;
}
Aber das bedeutet, dass alles immer und immer wieder zu kopieren ....: -/
Auch in moo()
, die const ist, kann ich joo()
nennen, welche xs
‚s Daten ändern wird. Nicht was ich wollte. Hier würde ich einen Kompilierungsfehler bevorzugen.
Der vollständige Code ist verfügbar unter ideone.com.
Die Frage ist: Ist es möglich, das gleiche zu tun, aber ohne den Vektor auf seine const-Version zu kopieren? Oder, allgemeiner, gibt es eine gute Technik/Muster, die sowohl effizient als auch einfach zu verstehen sind?
Vielen Dank. :-)
Get eine 'const'-Ansicht mit' boost :: adapters :: transformed' und einem passenden ate function-Objekt, um Ihre Shared-Pointer zu konvertieren. – Xeo
@Xeo: Ich habe mich schnell um 'boost :: adapter :: transformed' gekümmert, aber es scheint, dass ich etwas herum kopieren muss, ein bisschen wie oben, aber mit einer anderen Syntax, oder? Wenn es nicht der Fall ist, würden Sie gerne ein Beispiel geben? :-) – Hiura
Nur eine Anmerkung. Dein 'std :: move (i)' wird nichts bewegen. 'move' bewegt sich nicht, es ist nur eine Besetzung. Vielleicht ist es nur vom Kopieren Ihres tatsächlichen Codes, wohin es sich bewegt :) – typ1232