Das folgende kurze Programm
#include <vector>
#include <iostream>
std::vector<int> someNums()
{
return {3, 5, 7, 11};
}
class Woop
{
public:
Woop(const std::vector<int>& nums) : numbers(nums) {}
void report()
{
for (int i : numbers)
std::cout << i << ' ';
std::cout << '\n';
}
private:
const std::vector<int>& numbers;
};
int main()
{
Woop woop(someNums());
woop.report();
}
hat ein baumelndes Referenzproblem, vor dem kein Compiler zu warnen scheint. Das Problem ist, dass Provisorien an const-refs gebunden werden können, die Sie dann behalten können. Die Frage ist dann; Gibt es eine Methode, um dieses Problem zu vermeiden? Am besten eine, bei der es nicht darum geht, die Korrektheit der Konstanten zu opfern oder immer Kopien großer Objekte zu erstellen.
std::unique_ptr
für exklusives Eigentumstd::shared_ptr
oder für gemeinsames Eigentum oderstd::weak_ptr
zumindest, um verlorene Daten zu erkennen).-fsanitize=address
. Ich glaube nicht, dass es eine bewährte Methode gibt, um dies zu vermeiden, ohne die Leistung zu beeinträchtigen.Antworten:
In Situationen, in denen eine Methode nach der Rückgabe eine Referenz behält, empfiehlt es sich,
std::reference_wrapper
anstelle einer normalen Referenz Folgendes zu verwenden :Woop (std::vector<int> const &&) = delete;
für Ihre Methode verwendet:quelle
Eine Möglichkeit, Ihre Klasse weniger anfällig zu machen, könnte darin bestehen, einen gelöschten Konstruktor hinzuzufügen, der eine Rechtsreferenz benötigt. Dies würde Ihre Klasseninstanz daran hindern, Bindungen an temporäre Elemente herzustellen.
Dieser gelöschte Konstruktor würde tatsächlich dazu führen, dass der O / P-Code nicht kompiliert wird. Welches Verhalten könnten Sie suchen?
quelle
Ich stimme den anderen Antworten und Kommentaren zu, die Sie sorgfältig überlegen sollten, wenn Sie wirklich eine Referenz in der Klasse speichern müssen. Und wenn Sie dies tun, möchten Sie wahrscheinlich stattdessen einen nicht konstanten Zeiger auf einen konstanten Vektor (dh
std::vector<int> const * numbers_
).Wenn dies jedoch der Fall ist, finde ich, dass die anderen aktuell veröffentlichten Antworten nebensächlich sind. Sie alle zeigen Ihnen, wie Sie
Woop
diese Werte besitzen können.Wenn Sie sicherstellen können, dass der von Ihnen übergebene Vektor Ihre
Woop
Instanz überlebt , können Sie die Erstellung einesWoop
aus einem r-Wert explizit deaktivieren . Dies ist mit dieser C ++ 11-Syntax möglich:Jetzt wird Ihr Beispielcode nicht mehr kompiliert. Der Compiler mit gibt einen ähnlichen Fehler aus:
PS: Sie möchten wahrscheinlich einen expliziten Konstruktor, siehe z. B. Was bedeutet das explizite Schlüsselwort? .
quelle
Um diesen speziellen Fall zu verhindern, können Sie entweder einen Zeiger verwenden (da dies
Weep(&std::vector<int>{1,2,3})
nicht zulässig ist) oder eine nicht konstante Referenz verwenden, die auch bei einem temporären Fehler auftritt.Diese garantieren immer noch nicht, dass der Wert gültig bleibt, stoppen jedoch zumindest den einfachsten Fehler, erstellen keine Kopie und müssen nicht
nums
auf spezielle Weise erstellt werden (z. B. wiestd::shared_ptr
oderstd::weak_ptr
).std::scoped_lock
Ein Verweis auf den Mutex wäre ein Beispiel, und eines, bei dem ein eindeutiger / geteilter / schwacher ptr nicht wirklich erwünscht ist. Oft ist dasstd::mutex
nur ein Basismitglied oder eine lokale Variable. Sie müssen immer noch sehr vorsichtig sein, aber in diesen Fällen ist es im Allgemeinen einfach, die Lebensdauer zu bestimmen.std::weak_ptr
ist eine weitere Option für Nicht-Besitzer, aber dann zwingen Sie den Anrufer zur Verwendungshared_ptr
(und damit auch zur Heap-Zuweisung), und manchmal ist das nicht erwünscht.Wenn eine Kopie in Ordnung ist, wird das Problem nur vermieden.
If
Woop
sollte den Besitz übernehmen, entweder als r-Wert übergeben und verschieben (und Zeiger- / Referenzprobleme vollständig vermeiden) oder verwenden,unique_ptr
wenn Sie den Wert selbst nicht verschieben können oder möchten, dass der Zeiger gültig bleibt.Wenn das Eigentum geteilt wird, können Sie es
shared_ptr
für alles verwenden und es wird zusammen mit der endgültigen Referenz gelöscht. Dies kann jedoch dazu führen, dass das Verfolgen von Objektlebenszyklen bei Überbeanspruchung sehr verwirrend wird.quelle
Sie können verwenden
template programming
undarrays
wenn Sie ein Objekt haben möchten, das einenconst
Container enthält. Aufgrund desconstexpr
Konstruktorsconstexpr arrays
erreichen Sieconst correctness
undcompile time execution
.Hier ist ein Beitrag, der interessant sein könnte: std :: move a const vector
Code ausführen
Ausgabe:
quelle
std::array
diese garantiert kopiert, auch wenn sonst ein Zug möglich wäre. Darüber hinauswooping1
undwooping2
sind nicht der gleiche Typ, was weniger als ideal ist.