O C++23 deveria corrigir problemas com for
loops baseados em intervalo. O padrão C++ agora tem uma redação que posso rastrear até p2718r0 , incluindo o seguinte exemplo no final da seção "A instrução for baseada em intervalo [stmt.ranged]
" (observe o undefined behavior
comentário:
using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t) { return t; }
T g();
void foo() {
for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
for (auto e : f2(g())) {} // undefined behavior
}
Digamos que eu escreva uma corrotina usando o C++23 std::generator
que recebe um argumento por valor, ou seja, seguindo a regra CP.53 das diretrizes básicas do C++: Parâmetros para corrotinas não devem ser passados por referência :
#include <generator>
#include <string>
#include <iostream>
std::generator<std::string> generator_f(std::string x) {
co_yield x;
}
std::string sample_g() { return "abc"; }
int main() {
for(const auto& elem: generator_f(sample_g())) {
std::cout << elem << "\n";
}
return 0;
}
Presumo que, com base no exemplo do padrão C++, o código no for
loop main()
acima seja um comportamento indefinido.
Qual é a lógica por trás da regra de comportamento indefinido ilustrada pelo exemplo no padrão C++? A desvantagem é que ela torna o uso de std::generator
argumentos with em um loop propenso a erros (que é onde você gostaria de usar um gerador, ou seja, não corrige realmente problemas de tempo de vida com instruções for
baseadas em intervalo ).for
O comportamento indefinido neste código:
vem de
f2()
si mesmo:Esta função retorna uma referência ao seu parâmetro
t
, que é destruído ao final da chamada da função. Isso não está de forma alguma vinculado à expressãof2(g())
.A diferença neste código:
é que
f1()
retorna uma referência ao objeto temporário retornado porg()
, que dura até o final da expressão completa que o cria. Mas, anteriormente, essa expressão completa era a declaração da variável de intervalo gerada pelo compilador, não o loop propriamente dito. A mudança para o C++23 é que agora o tempo de vida desse temporário é estendido por todo o loop.Essa não é a situação com
f2()
at.f2()
não está retornando uma referência ao objeto temporário retornado porg()
, mas sim uma referência ao seu próprio parâmetrot
. Estender o tempo de vida do objeto temporário retornado porg()
durante todo o loop não resolve o problema.Por outro lado, este código:
sempre funcionou bem. Mesmo antes da P2718. Não há sequer referências vinculativas a temporários neste exemplo.