Question

Ma question (qui suivra après cela, désolé la longue intro, la question est là-bas dans gras ) est inspiré à l'origine par l'article 23 Herb Sutters Exceptionnel C ++ où l'on trouve quelque chose comme ceci:


...
int main()
{
  GenericTableAlgorithm a( "Customer", MyWorker() );
  a.Process();
}

avec


class GenericTableAlgorithm
{
public:
  GenericTableAlgorithm( const string& table,
                         GTAClient&    worker );
  bool Process(); 
private:
  struct GenericTableAlgorithmImpl* pimpl_; //implementation
};
class GTAClient
{
   ///...
   virtual bool ProcessRow( const PrimaryKey& ) =0;
   //...
};
class MyWorker : public GTAClient 
{
  // ... override Filter() and ProcessRow() to
  //     implement a specific operation ...
};


Maintenant, j'ai les problèmes suivants avec ce code (et non, je doute nullement les prouesses de M. Sutter comme expert C ++):

    1. L'exemple comme ça ne fonctionnera pas, car GTAClient et travailleur est un non const référence qui ne peut pas prendre un temporaire, mais bien, il aurait pu être écrit avant standard ou une faute de frappe, peu importe, ce n'est pas mon point.
    2. Ce qui me fait me demander ce qu'il va faire avec la référence des travailleurs, même si le problème 1. est ignoré.
      De toute évidence, l'intention est d'avoir MyWorker utilisé dans le NVI de GenericTableAlgorithm accessible par l'interface GTAClient (polymorphes); ce qui exclut que la mise en œuvre est propriétaire d'un membre de type GTAClient (valeur), car cela causerait tranchage de valeur sémantique, etc. ne se mélangent pas bien avec le polymorphisme.
      Il ne peut pas avoir un membre de données de type MyWorker soit depuis cette classe est inconnue GenericTableAlgorithm.
      Je conclus donc il doit avoir été destiné à être utilisé par pointeur ou référence, en conservant l'objet d'origine et de la nature polymorphique.
    3. Étant donné que des pointeurs vers des objets temporaires (MyWorker()) sont rarement une bonne idée, je suppose que le plan de l'auteur était d'utiliser la durée de vie prolongée de temporaires liés à des références (const), et stocker une telle référence dans les points de pimpl_ d'objet et de l'utiliser à partir de là. ( Note:. Il n'y a pas non plus fonction clone-membre dans GTAClient, qui aurait pu faire ce travail, nous supposons pas là est une usine à base typeinfo-RTTI qui se cache en arrière-plan)
      Et voici mes ensembles de questions en (enfin!): (Comment) peut passer un membre temporaire à une référence de classe avec durée de vie prolongée être fait légalement


    La norme dans §12.2.5 (la version C ++ 0x, mais il est le même en C ++, ne connaissent pas le numéro de chapitre) fait l'exception suivante de l'extension de la durée de vie: "-. Une borne temporaire à une référence élément dans un constructeur de cteur-initialiseur (12.6.2) persiste jusqu'à ce que les sorties du constructeur"

    Par conséquent, l'objet ne peut pas être utilisé dans l'appel du code client A.Procédé (); parce que le REFERENCEE temporaire MyWorker() est déjà mort

    Considérons maintenant un exemple de mon propre craft qui illustre le problème (testé sur GCC4.2):

    #include <iostream>
    using std::cout; 
    using std::endl;
    
    struct oogie {
     ~oogie()
     {
      cout << "~oogie():" << this << ":" << m_i << endl;
     }
     oogie(int i_)
      : m_i(i_)
     {
      cout << "oogie():" << this << ":" << m_i << endl;
     }
    
     void call() const
     {
      cout << "call(): " << this << ":" << m_i << endl;
     }
     int m_i;
    };
    
    oogie func(int i_=100)
    {
     return oogie(i_);
    }
    
    struct kangoo 
    {
     kangoo(const oogie& o_)
     : m_o(o_)
     {
     }
    
     const oogie& m_o;
    };
    
    int main(int c_, char ** v_)
    {
    
     //works as intended
     const oogie& ref = func(400);
     //kablewy machine
     kangoo s(func(1000));
    
     cout << ref.m_i << endl;
    
     //kangoo's referenced oogie is already gone
     cout << s.m_o.m_i << endl;
    
     //OK, ref still alive
     ref.call();
     //call on invalid object
     s.m_o.call();
    
     return 0;
    }
    

    qui produit la sortie

    oogie():0x7fff5fbff780:400
    oogie():0x7fff5fbff770:1000
    ~oogie():0x7fff5fbff770:1000
    400
    1000
    call(): 0x7fff5fbff780:400
    call(): 0x7fff5fbff770:1000
    ~oogie():0x7fff5fbff780:400
    

    Vous pouvez voir que dans le cas de const Oogie & ref immédiatement lié à valeur de référence de retour temporaire de func () a la durée de vie prolongée de ladite référence (jusqu'à la fin du principal), donc c'est bon.
    MAIS: L'objet 1000 Oogie est déjà détruite juste après a été construit kangoo-s. Le code fonctionne , mais nous avons affaire à un objet mort-vivant ici ...

    Donc, pour poser la question à nouveau:
    Tout d'abord, Suis-je manque quelque chose ici et le code est? correcte / juridique .
    En second lieu, pourquoi GCC me donner aucun avertissement de cela, même avec -Wall spécifié? Devrait-il? Se pourrait-il?

    Merci pour votre temps,
    martin

        
  • Était-ce utile?

    La solution

    Je pense que cela est une partie délicate qui est pas trop clair. Il y avait une question similaire juste un il y a quelques jours.

    Par défaut sont temporaires détruits dans l'ordre inverse de la construction lorsque la pleine expression dans laquelle ils ont été créés finalise. Jusqu'ici tout va bien et compris, mais des exceptions découlent (12.2 [class.temporary] / 4,5) et les choses deviennent confus.

    Au lieu de traiter le libellé exact et définitions de la norme, je vais aborder le problème du point de vue technique / compilateur. Temporaries sont créés dans la pile, quand une fonction complète le cadre de pile est libéré (le pointeur de pile est déplacé vers la position d'origine avant l'appel de fonction démarré).

    Cela implique qu'un temporaire ne peut jamais survivre à la fonction dans laquelle il a été créé. Plus exactement, il ne peut pas survivre à la portée où il a été défini, même si elle peut en effet survivre à la pleine expression dans lequel il a été créé.

    Aucune des exceptions à la norme relève de cette restriction, dans tous les cas la durée de vie du temporaire est étendue à un point qui est garanti de ne pas dépasser l'appel de fonction dans laquelle le temporaire a été créé.

    Autres conseils

    Le Guru original de l'article Semaine est ici: Guru de la semaine # 15 .

    Une partie de votre réponse pourrait venir de Herb lui-même, "un candidat pour le plus important const" - la. "partie const" d'attribution temporaire à une référence est en effet important

    Il semble que si ce bogue dans l'article original.

    J'ai toujours considéré que le passage des paramètres d'adresse (que ce soit par référence ou pointeur) nécessitait une compréhension de la durée de vie de la chose passée. Période. Fin de la conversation. Cela semble juste un attribut d'un environnement non-ordures collectées / NOT-référence gérés.

    Ceci est l'un des avantages du GC.

    Dans c ++ parfois les problèmes de vie ont été résolus par:

    X :: clone

    ou par une documentation explicite de l'interface, par exemple:

    « il appartient à la instantiateur de Y pour faire en sorte que l'instance de X passé en paramètre x existe pendant toute la durée de vie de Y »

    ou

    copie explicite de x dans les entrailles du récepteur.

    C'est juste c ++. Ce c ++ ajouté une garantie sur les références const était agréable (et vraiment peu nécessaire), mais qui était cela.

    Ainsi, je considère le code comme indiqué dans la question à tort et je suis d'avis qu'il aurait dû être:

    int main()
    {
      MyWorker w;
      GenericTableAlgorithm a( "Customer", w);
      a.Process();
    }
    

    Je ne pense pas que vous pouvez le faire avec le courant C ++. Vous avez besoin du rel="nofollow déplacer la sémantique qui sera introduit dans C de la x0.

    Licencié sous: CC-BY-SA avec attribution
    Non affilié à StackOverflow
    scroll top