Il seguente codice sembra piuttosto innocuo a prima vista. Un utente usa la funzione bar()
per interagire con alcune funzionalità della libreria. (Potrebbe anche funzionare per un lungo periodo da quando bar()
ha restituito un riferimento a un valore non temporaneo o simile.) Ora tuttavia si tratta semplicemente di restituire una nuova istanza di B
. B
ha di nuovo una funzione a()
che restituisce un riferimento a un oggetto del tipo iterabile di A
. L'utente desidera interrogare questo oggetto che conduce a un segfault poiché l'oggetto B
temporaneo restituito da bar()
viene distrutto prima dell'inizio dell'iterazione.
Sono indeciso a chi (biblioteca o utente) è da biasimare per questo. Tutte le classi fornite dalle librerie mi sembrano pulite e certamente non stanno facendo nulla di diverso (restituendo riferimenti ai membri, restituendo istanze di stack, ...) di tanto altro codice là fuori. Anche l'utente non sembra fare nulla di sbagliato, sta semplicemente iterando su qualche oggetto senza fare nulla riguardo alla durata di questi oggetti.
(Una domanda correlata potrebbe essere: si dovrebbe stabilire la regola generale che il codice non dovrebbe "range-based-for-iterate" su qualcosa che viene recuperato da più di una chiamata concatenata nell'intestazione del loop poiché una di queste chiamate potrebbe restituire un valore?)
#include <algorithm>
#include <iostream>
// "Library code"
struct A
{
A():
v{0,1,2}
{
std::cout << "A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
int * begin()
{
return &v[0];
}
int * end()
{
return &v[3];
}
int v[3];
};
struct B
{
A m_a;
A & a()
{
return m_a;
}
};
B bar()
{
return B();
}
// User code
int main()
{
for( auto i : bar().a() )
{
std::cout << i << std::endl;
}
}