Poniższy krótki program
#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();
}
ma wiszący problem z referencją, o którym żaden kompilator nie ostrzega. Problem polega na tym, że elementy tymczasowe można powiązać z ograniczeniami, które następnie można zachować. Pytanie zatem brzmi; Czy istnieje metoda uniknięcia tego problemu? Najlepiej taki, który nie wymaga poświęcenia stałej poprawności lub wykonania kopii dużych obiektów.
std::unique_ptr
dla wyłącznej własnościstd::shared_ptr
lub współwłasności, lubstd::weak_ptr
przynajmniej do rozpoznania utraconych danych).-fsanitize=address
. Nie sądzę, aby istniała jakaś najlepsza praktyka, aby tego uniknąć bez poświęcania wydajności.Odpowiedzi:
W sytuacji, gdy jakaś metoda zachowuje referencję po zwrocie, dobrym pomysłem jest użycie
std::reference_wrapper
zamiast normalnej referencji:Woop (std::vector<int> const &&) = delete;
dla twojej metody:źródło
Jednym ze sposobów, aby uczynić swoją klasę mniej podatną na zagrożenia, może być dodanie usuniętego konstruktora, który wymaga poprawnego odwołania. To powstrzyma instancję klasy przed tworzeniem powiązań z tymczasami.
Ten usunięty konstruktor sprawiłby, że kod O / P nie zostałby skompilowany, co może być zachowaniem, którego szukasz?
źródło
Zgadzam się z innymi odpowiedziami i komentarzami, które powinieneś dokładnie przemyśleć, jeśli naprawdę potrzebujesz przechowywać referencje w klasie. A jeśli to zrobisz, prawdopodobnie będziesz chciał zamiast wskaźnika const do wektora stałego (tj
std::vector<int> const * numbers_
.).Jeśli jednak tak jest, stwierdzam, że inne obecnie opublikowane odpowiedzi nie mają znaczenia. Wszystkie pokazują, jak tworzyć
Woop
własne wartości.Jeśli możesz upewnić się, że wektor, który przekazujesz, przeżyje
Woop
instancję, możesz jawnie wyłączyć konstruowanie aWoop
na podstawie wartości. Jest to możliwe przy użyciu tej składni C ++ 11:Twój przykładowy kod nie będzie się już kompilował. Kompilator podający błąd podobny do:
PS: Prawdopodobnie chcesz jawnego konstruktora, patrz np. Co oznacza to słowo kluczowe? .
źródło
Aby uniknąć tego konkretnego przypadku, możesz wybrać wskaźnik (ponieważ
Weep(&std::vector<int>{1,2,3})
jest to niedozwolone) lub możesz wybrać odniesienie inne niż stałe, które również będzie tymczasowo powodować błąd.Nadal nie gwarantują one, że wartość pozostaje ważna, ale powstrzymuje przynajmniej najłatwiejszy błąd, nie tworzy kopii i nie musi
nums
być tworzona w specjalny sposób (np. Jakostd::shared_ptr
lubstd::weak_ptr
robi).std::scoped_lock
odwołanie się do muteksu byłoby przykładem, a taki, w którym unikalne / wspólne / słabe ptr nie jest tak naprawdę potrzebne. Częstostd::mutex
będzie to tylko podstawowy element członkowski lub zmienna lokalna. Nadal musisz być bardzo ostrożny, ale w takich przypadkach zazwyczaj łatwo jest określić długość życia.std::weak_ptr
jest kolejną opcją dla nie posiadających, ale wtedy zmuszasz osobę wywołującą do używaniashared_ptr
(a tym samym do przydzielania sterty), a czasem nie jest to pożądane.Jeśli kopia jest w porządku, to po prostu pozwala uniknąć problemu.
Jeśli
Woop
należy przejąć na własność albo przekazać jako wartość r i przenieść (i całkowicie unikać problemów ze wskaźnikiem / referencją), albo użyć,unique_ptr
jeśli nie można przenieść samej wartości lub chcesz, aby wskaźnik pozostał ważny.Lub jeśli własność jest współdzielona, możesz użyć
shared_ptr
do wszystkiego, a zostanie ona usunięta wraz z ostatecznym odniesieniem, ale może to spowodować, że śledzenie cykli życia obiektu stanie się bardzo mylące, jeśli zostanie nadmiernie wykorzystane.źródło
Możesz użyć
template programming
iarrays
jeśli chcesz mieć obiekt, który zawieraconst
kontener. Dziękiconstexpr
konstruktorowiconstexpr arrays
osiągaszconst correctness
icompile time execution
.Oto post, który może być interesujący: std :: move a const vector
uruchom kod
Wynik:
źródło
std::array
gwarantują kopiowanie, nawet jeśli ruch byłby w przeciwnym razie dostępny. Na dodatekwooping1
iwooping2
nie są tego samego typu, co jest dalekie od ideału.