2010-11-03 3 views
3

J'ai une question sur la conformité à la norme C++ ou son absence.Référence constante pour l'allongement temporaire de la durée de vie

Dans mon projet, j'utilise une classe Guard simple qui utilise l'astuce de référence const. J'utilise Visual Studio 2005 et il existe deux configurations: une pour la version normale et la seconde pour les tests unitaires.

Dans les deux cas, il y a une suspension temporaire sur la référence const à la fin, mais ce qui se passe entre-temps, c'est le problème. Pour la configuration de version, la référence const pointe directement sur la température créée dans le retour du modèle de fonction d'assistance qui crée l'instance de Guard (aucun constructeur de copie n'est appelé, même pas instancié d'ailleurs).

Mais pour le test d'unité conf, le modèle de fonction temp est d'abord copié, puis son destructeur est appelé, en faisant ce qui doit être fait seulement après que la référence const est hors de portée.

J'ai résolu le problème en désactivant la garde d'origine dans le constructeur de copie de classe de base (donc l'action destructor ne se déclenche pas pour config pour laquelle constructeur de copie est appelé), mais ce qui me dérange est:

Est-ce le Comportement de copie-le-temporaire conforme aux normes? La norme indique-t-elle que la référence const doit pointer directement sur la température, ou ce comportement défini par l'implémentation n'est-il pas spécifié dans la norme?

J'ai basé mon code en gros sur l'article de Scope Guard dans DDJ et l'article de Herb Sutter sur gotw 88, mais ces deux sources ne semblent pas tenir compte de l'appel du destructeur précédent.

Toute information de quelqu'un de mieux informé sera appréciée.

EDIT:

Ok le code est quelque chose comme ceci:

class GuardBase 
{ 
public: 

    GuardBase() : m_enabled(true) 
    {} 

    //this is done because in normal build no copy constructor is called (directly using the function temporary) 
    //but for UT conf somehow the original temp is copied and destroyed 
    GuardBase(const GuardBase& other) 
    { 
    other.disable(); 
    } 

    void disable() const 
    { 
    m_enabled = false; 
    } 

protected: 
    //member is mutable because we will access the object through the const reference 
    mutable bool m_enabled; 
}; 

template< typename Arg, typename ObjType, typename MemberMethod > 
class Guard1Arg : public GuardBase 
{ 
public: 
    Guard1Arg(ObjType& obj, MemberMethod remover, Arg arg) : m_arg(arg), m_remover(remover), m_object(obj) 
    {} 

    ~Guard1Arg() 
    { 
    if (m_enabled) 
    { 
     (m_object.*m_remover)(m_arg); 
    } 
    } 

private: 
    Arg m_arg; 
    MemberMethod m_remover; 
    ObjType& m_object; 

    //this class should not be assigned 
    Guard1Arg& operator=(const Guard1Arg& other); 

}; 

//utility template function used to create Guards using member functions with 1 argument 
template<typename MemberFunction, typename Obj, typename Arg> 
Guard1Arg<Arg, Obj, MemberFunction> MakeGuard1Arg(Obj& obj, MemberFunction memberFunction, Arg& arg) 
{ 
    return Guard1Arg<Arg, Obj, MemberFunction>(obj, memberFunction, arg); 
} 


#define GUARD_CREATE(arg, remover) const GuardBase& guard = MakeGuard1Arg(*this, remover, arg); 
#define GUARD_DISABLE guard.disable(); 
#define GUARD_FRIEND template< typename Arg, typename ObjType, typename MemberMethod > friend class Guard1Arg; 
+1

Vous avez un code pour illustrer cela? – sharptooth

+0

Sans le code, il n'est pas possible de dire exactement. Toutes les réponses seront des suppositions. –

Répondre

4

Les deux comportements sont des normes conformes. Si vous avez du code comme ceci:

T foo() 
{ 
    return T(); 
} 

int main() 
{ 
    const T& x = foo(); 
} 

Ensuite, sur le plan conceptuel, en foo, est créé un objet temporaire. Ce temporaire est copié à la valeur de retour de foo. Dans main, cette copie (qui est également un objet temporaire) est liée à x.
La copie qui est la valeur de retour de foo obtient sa durée de vie étendue, mais pas le temporaire qui était la source pour la copie. Mais, la norme C++ autorise explicitement l'élution d'objets temporaires redondants. Ainsi, au lieu de créer un temporaire et de le copier dans l'emplacement pour les valeurs de retour, foo peut directement créer le temporaire dans cet emplacement.
Les deux options sont possibles, et le compilateur n'a même pas besoin de documenter quand il utilise quelle option.

Les sections pertinentes de la norme C++ sont 6.6.3 ([stmt.return]) et 12.2 ([class.temporary]).

+1

Ok, j'ai une fois de plus parcouru l'article ScopeGuard et en effet il utilise la même solution que moi - en désactivant l'autre garde dans le constructeur de copie de la classe de base.Je dois avoir omis cette partie :). Néanmoins, l'article de Sutter ne mentionne nulle part la possibilité que le destructeur soit appelé deux fois. Merci beaucoup pour cela. – altariste

Questions connexes