Le code suivant semble plutôt inoffensif à première vue. Un utilisateur utilise la fonction bar()
pour interagir avec certaines fonctionnalités de la bibliothèque. (Cela peut même avoir fonctionné pendant longtemps depuis qu'il a bar()
renvoyé une référence à une valeur non temporaire ou similaire.) Maintenant, cependant, il renvoie simplement une nouvelle instance de B
. B
a à nouveau une fonction a()
qui renvoie une référence à un objet de type itératif A
. L'utilisateur souhaite interroger cet objet, ce qui entraîne une erreur de segmentation, car l' B
objet temporaire renvoyé par bar()
est détruit avant le début de l'itération.
Je ne sais pas qui (bibliothèque ou utilisateur) est à blâmer pour cela. Toutes les classes fournies par la bibliothèque me semblent propres et ne font certainement rien de différent (renvoyer des références aux membres, renvoyer des instances de pile, ...) que tant d'autres codes. L'utilisateur ne semble pas faire de mal non plus, il itère simplement sur un objet sans rien faire concernant la durée de vie de cet objet.
(Une question connexe pourrait être: Devrait-on établir la règle générale selon laquelle le code ne doit pas "plage basée sur itération" sur quelque chose qui est récupéré par plusieurs appels chaînés dans l'en-tête de la boucle, car l'un de ces appels peut renvoyer un rvalue?)
#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;
}
}