Załóżmy, że mam generate_my_range
klasę, która modeluje range
(w szczególności jest regular
). Czy następujący kod jest poprawny:
auto generate_my_range(int some_param) {
auto my_transform_op = [](const auto& x){ return do_sth(x); };
return my_custom_rng_gen(some_param) | ranges::views::transform(my_transform_op);
}
auto cells = generate_my_range(10) | ranges::to<std::vector>;
Czy jest my_custom_rng_gen(some_param)
pobierany przez wartość (pierwszy) operator potoku, czy też mam zwisające odniesienie po opuszczeniu generate_my_range
zakresu?
Czy byłoby tak samo z wywołaniem funkcji ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)
?
Czy poprawne byłoby użycie odniesienia do wartości? na przykład:
auto generate_my_range(int some_param) {
auto my_transform_op = [](const auto& x){ return do_sth(x); };
auto tmp_ref = my_custom_rng_gen(some_param);
return tmp_ref | ranges::views::transform(my_transform_op);
}
Jeśli zakresy są uwzględniane przez wartości dla tych operacji, to co mam zrobić, jeśli przekażę odwołanie do wartości do kontenera? Czy powinienem użyć ranges::views::all(my_container)
wzoru?
Odpowiedzi:
W bibliotece zakresów istnieją dwa rodzaje operacji:
Widoki są lekkie. Przekazujesz je według wartości i wymagasz, aby kontenery leżące poniżej były ważne i niezmienione.
Z dokumentacji zakresów-v3
i:
Zniszczenie leżącego pod nim kontenera oczywiście unieważnia wszystkie iteratory.
W swoim kodzie używasz widoków - korzystasz
ranges::views::transform
. Fajka jest cukrem syntaktycznym, co ułatwia pisanie takim, jakim jest. Powinieneś spojrzeć na ostatnią rzecz w rurze, aby zobaczyć, co produkujesz - w twoim przypadku jest to widok.Gdyby nie było operatora potoku, prawdopodobnie wyglądałby mniej więcej tak:
gdyby w ten sposób było połączonych wiele transformacji, można zobaczyć, jak brzydko by to zrobiło.
Tak więc, jeśli
my_custom_rng_gen
produkuje jakiś kontener, który przekształcasz, a następnie powrócisz, kontener ten zostanie zniszczony i będziesz mieć wiszące odniesienia z twojego widoku. Jeślimy_custom_rng_gen
jest inny widok kontenera, który mieszka poza tymi zakresami, wszystko jest w porządku.Jednak kompilator powinien być w stanie rozpoznać, że stosujesz widok do tymczasowego kontenera i uderzył cię błąd kompilacji.
Jeśli chcesz, aby funkcja zwróciła zakres jako kontener, musisz jawnie „zmaterializować” wynik. W tym celu użyj
ranges::to
operatora w ramach funkcji.Aktualizacja: Aby uzyskać więcej informacji na temat komentarza „gdzie dokumentacja mówi, że komponowanie zakresu / orurowania bierze i przechowuje widok?”
Fajka jest jedynie cukrem syntaktycznym, który łączy rzeczy w łatwy do odczytania sposób. W zależności od tego, jak jest używany, może on zwrócić widok. To zależy od argumentu po prawej stronie. W twoim przypadku jest to:
Zatem wyrażenie zwraca cokolwiek, co
views::transform
zwraca.Teraz czytając dokumentację transformacji:
Dlatego zwraca zakres, ale ponieważ jest to operator leniwy, że zakres zwraca się widokiem, ze wszystkimi jego semantyki.
źródło
ranges::views::all(my_container)
? A jeśli widok zostanie przekazany do rury? Czy rozpoznaje, że przekazano mu kontener lub widok? Czy to konieczne? W jaki sposób?my_custom_rng_gen
. Jak dokładnie rura itransform
interakcja pod maską nie jest ważne. Całe wyrażenie przyjmuje zakres jako argument (kontener lub widok do jakiegoś kontenera) i zwraca inny widok do tego kontenera. Zwracana wartość nigdy nie będzie właścicielem kontenera, ponieważ jest to widok.Na podstawie dokumentacji zakresów-v3 :
i
Ponieważ powiedziałeś, że zakres tymczasowy może być traktowany jako kontener, twoja funkcja zwraca wiszące odniesienie.
Innymi słowy, musisz upewnić się, że podstawowy zakres przeżywa widok, w przeciwnym razie masz kłopoty.
źródło