Tenho uma função que retorna um lambda.
auto make_scanner(std::vector<int> const &v) {
auto begin = v.cbegin();
auto end = v.cend();
return [begin, end] () mutable -> int {
return (begin != end) ? *begin++ : 0;
};
}
Note que o lambda captura iteradores no vetor, mas não o vetor em si. No caso de uso comum, isso é bom:
// Common Use Case
std::vector<int> data = {1, 2, 3};
auto scanner = make_scanner(data);
for (int x = scanner(); x != 0; x = scanner()) {
// compute something
}
Mas se o chamador fizer um scanner a partir de um vetor temporário sem nome, o lambda ficará com iteradores pendentes:
// Incorrect Use Case
auto scanner = make_scanner(std::vector<int>{1, 2, 3});
// Invoking scanner() is UB because the iterators reference
// the vector after its lifetime.
Em uma compilação não otimizada (ou seja, uma compilação de depuração), isso geralmente funcionará como pretendido, mesmo que o código esteja errado. Em uma compilação otimizada, se você tiver sorte, isso travará ou pelo menos fará com que os testes falhem.
Existe uma maneira de falhar a compilação quando make_scanner é chamado com um vetor temporário sem nome e ainda permitir o caso de uso comum?
Tenho tentado várias sobrecargas com referências de valor r, mas a resposta não me vem à mente.
(Sim, eu percebo que é possível usar um vetor não temporário e ainda acabar com iteradores pendurados no lambda. Não estou preocupado com esses casos neste momento.)
Isso é bem fácil de conseguir alavancando funções deletadas. A primeira coisa que precisamos saber é que ao sobrecarregar uma função o parâmetro
T&&
tem precedência sobre uma sobrecarga com o parâmetroconst T&
quando a função é chamada com um rvalue.Se você excluir a sobrecarga de referência rvalue, a resolução de sobrecarga escolherá essa função para rvalues e você receberá um erro do compilador por usar uma função excluída.
Para o seu código que se torna
e você pode ver isso funcionando neste exemplo ao vivo .
Você pode fornecer uma sobrecarga de rvalue excluído.
Você também pode fazer uma sobrecarga de trabalho para o vetor rvalue, ele teria que mover o vetor dentro do lambda:
Aqui está uma demonstração.