56 votes

Pourquoi à l'aide d'un objet temporaire dans la gamme pour un initialiseur de provoquer un accident?

Pourquoi le code suivant crash à la fois sur Visual Studio et de GCC?

Pour le blocage, il exige de la gamme à base de boucle, std::map, std::string et en prenant une référence à la chaîne. Si je supprime un d'entre eux pour qu'il fonctionne.

#include <iostream>
#include <string>
#include <map>
using namespace std;

struct S
{
    map<string, string> m;

    S()
    {
        m["key"] = "b";
    }

    const string &func() const
    {
        return m.find("key")->second;
    }
};

int main()
{
    for (char c : S().func())
        cout << c;

    return 0;
}

Ideone lien: http://ideone.com/IBmhDH

67voto

Yakk Points 31636

La plage de l'initialisation de la ligne de for(:) boucle ne pas prolonger la durée de vie de rien, mais la finale temporaire (le cas échéant). Toute autre temporaires sont supprimées avant la for(:) boucle en cours d'exécution.

Maintenant, ne désespérez pas; il y a une solution facile à ce problème. Mais d'abord, une promenade à travers de ce qui ne va pas.

Le code for(auto x:exp){ /* code */ } s'étend, en gros:

{
  auto&& __range=exp;
  auto __it=std::begin(__range);
  auto __end=std::end(__range);
  for(; __it!=__end;++__it){
    auto x=*__it;
    /* code */
  }
}

(Avec un modeste se trouve sur l' __it et __end lignes, et toutes les variables commençant par __ ont clairement pas de nom. Aussi, je suis en train de montrer C++17 version, parce que je crois en un monde meilleur, et les différences n'ont pas d'importance ici).

Votre exp crée un objet temporaire, puis renvoie une référence à l'intérieur d'elle. Le temporaire meurt au bout de la ligne, de sorte que vous avez une balançant de référence dans le reste du code.

C'est la fixation est relativement facile. Pour résoudre ce problème:

std::string const& func() const& // notice &
{
    return m.find("key")->second;
}
std::string func() && // notice &&
{
    return std::move(m.find("key")->second);
}

ne rvalue les surcharges et retour, s'installe en valeurs par la valeur lors de la consommation d'temporaires au lieu de retourner des références dans.

Puis le

auto&& __range=exp;

ligne de référence durée de vie de l'extension sur la valeur retournée string, et de plus en balançant les références.

En règle générale, ne jamais retourner une plage par référence à un paramètre qui pourrait être une rvalue.


Annexe: Attente, && et const& après les méthodes? références rvalue d' *this?

C++11 a ajouté des références rvalue. Mais l' this ou auto paramètre de fonctions spéciales. Pour sélectionner la surcharge d'une méthode fondée sur la valeur r/lvalue-ness de l'objet invoqué, vous pouvez utiliser & ou && après la fin de la méthode.

Cela fonctionne comme le type d'un paramètre à une fonction. && après la méthode etats, que la méthode doit être appelée uniquement sur la non-const rvalues; const& - dire qu'il doit être appelé pour constant lvalues. Des choses qui ne correspondent pas exactement suivent le precidence règles.

Lorsque vous avez une méthode qui retourne une référence vers un objet, assurez-vous d'attraper temporaires avec un && de surcharge et de ne pas renvoyer une référence dans ces cas (retourner une valeur), ou =delete la méthode.

32voto

Sam Varshavchik Points 2563
S().func()

Ceci construit un objet temporaire, et appelle une méthode qui retourne une référence à un std::string appartenant (indirectement) par l'objet temporaire ( std::string est dans le conteneur qui est une partie de l'objet temporaire).

Après l'obtention de la référence, l'objet temporaire est détruit. Cela détruit l' std::string qui a été la propriété (indirectement) par l'objet temporaire.

Après ce point, toute autre utilisation de l'objet référencé devient un comportement indéfini. Tel que de parcourir son contenu.

C'est un très commun piège, quand il vient à l'aide de la gamme d'itération. Sincèrement vôtre est également coupable de se trébucher.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X