Suponha que eu tenha uma generate_my_range
classe que modela a range
(em particular, é regular
). Então, o seguinte código está correto:
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>;
É my_custom_rng_gen(some_param)
valorizada pelo (primeiro) operador de tubulação ou tenho uma referência pendente quando saio do generate_my_range
escopo?
Seria o mesmo com a chamada funcional ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)
?
Seria correto se eu usasse uma referência lvalue? por exemplo:
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);
}
Se intervalos são obtidos por valores para essas operações, o que devo fazer se passar um lvalue ref para um contêiner? Devo usar um ranges::views::all(my_container)
padrão?
Respostas:
Na biblioteca de intervalos, existem dois tipos de operações:
As visualizações são leves. Você os passa por valor e exige que os contêineres subjacentes permaneçam válidos e inalterados.
Na documentação do range-v3
e:
A destruição do contêiner subjacente obviamente invalida todos os iteradores para ele.
Em seu código você está specifially usando visualizações - Você usa
ranges::views::transform
. O cachimbo é apenas um açúcar sintático para facilitar a escrita do jeito que está. Você deve olhar a última coisa no tubo para ver o que produz - no seu caso, é uma visão.Se não houvesse operador de tubo, provavelmente seria algo parecido com isto:
se houvesse várias transformações conectadas dessa maneira, você poderá ver como seria feio.
Assim, se
my_custom_rng_gen
produz algum tipo de contêiner, que você transforma e depois retorna, esse contêiner é destruído e você tem referências pendentes da sua exibição. Semy_custom_rng_gen
houver outra visão de um contêiner que mora fora desses escopos, está tudo bem.No entanto, o compilador deve poder reconhecer que você está aplicando uma exibição em um contêiner temporário e receber um erro de compilação.
Se você deseja que sua função retorne um intervalo como um contêiner, é necessário "materializar" explicitamente o resultado. Para isso, use o
ranges::to
operador dentro da função.Atualização: para ser mais explícito em relação ao seu comentário "onde a documentação diz que a faixa / tubulação de composição obtém e armazena uma visualização?"
Pipe é apenas um açúcar sintático para conectar coisas em uma expressão fácil de ler. Dependendo de como é usado, ele pode ou não retornar uma exibição. Depende do argumento do lado direito. No seu caso, é:
Portanto, a expressão retorna o que quer que
views::transform
retorne.Agora, lendo a documentação da transformação:
Portanto, ele retorna um intervalo, mas, como é um operador lento, esse intervalo é uma visualização, com toda a sua semântica.
fonte
ranges::views::all(my_container)
? E se uma visão for passada para o pipe? Ele reconhece que recebeu um contêiner ou uma exibição? Precisa? Quão?my_custom_rng_gen
. Como exatamente o tubo etransform
interagir sob o capô não é importante. A expressão inteira assume um intervalo como argumento (um contêiner ou uma exibição para algum contêiner) e retorna uma exibição diferente para esse contêiner. O valor de retorno nunca será o proprietário do contêiner, porque é uma visualização.Retirado da documentação do range-v3 :
e
Como você disse que o intervalo temporário pode ser considerado um contêiner, sua função retorna uma referência pendente.
Em outras palavras, você precisa garantir que o intervalo subjacente sobreviva à visualização ou esteja com problemas.
fonte