2010-11-29 26 views
5

Il y a longtemps que je l'ai créé un modèle suivant pour que je sois un assert chaque fois que je joue un static_cast mais le type est pas ce que je suppose qu'il soit:Contrôlé fonte statique sur une référence

/// perform a static_cast asserted by a dynamic_cast 
template <class Type, class SourceType> 
Type static_cast_checked(SourceType item) 
{ 
    Assert(!item || dynamic_cast<Type>(item)); 
    return static_cast<Type>(item); 
} 

Aujourd'hui, je voulais pour créer une variante qui ne fonctionnerait pas seulement avec des pointeurs, mais avec des références ainsi:

/// overload for reference 
template <class Type, class SourceType> 
Type &static_cast_checked(SourceType &item) 
{ 
    Assert(dynamic_cast<Type *>(&item)); 
    return static_cast<Type>(item); 
} 

Cependant, le compilateur ne semble pas utiliser cette surcharge lorsque je cASTING une référence à une autre référence. J'ai peur de ne pas comprendre suffisamment les règles de résolution des templates pour comprendre pourquoi ou pour créer une variante qui fonctionne.

Remarque: Je ne peux pas attraper le bad_cast exception au lieu de vérifier dynamic_cast<Type *> pour NULL, les exceptions étant désactivées pour ce projet.

+1

Pouvez-vous ajouter une surcharge pour '' SourceType * et retirer l'original? Avez-vous besoin de prendre en charge des types de valeur du tout? (Et est-ce que cela corrige le problème, je me demande maintenant?) –

+1

J'ai pensé à ces lignes de code, et je ne peux pas comprendre pourquoi cela est nécessaire. Si vous vérifiez un casting statique avec un casting dynamique, seuls les objets peuvent être vérifiés statiquement casted avec castable dynamique. Cela signifie qu'il s'agit d'une réimplantation de 'dynamic_cast'. La différence entre la distribution dynamique et statique est que la distribution dynamique examine le vtable et le statique non. – nutario

+0

Il s'agit d'un cast statique, mais qui affirme - c'est-à-dire que vous notifiez le débogage que votre hypothèse sur le type que vous lancez est fausse. – Suma

Répondre

5

Supprimer * et & des types de retour:

/// perform a static_cast asserted by a dynamic_cast 
template <class Type, class SourceType> 
Type static_cast_checked(SourceType *item) 
{ 
    Assert(!item || dynamic_cast<Type>(item)); 
    return static_cast<Type>(item); 
} 

template <class Type> struct make_pointer 
{ 
    typedef Type *PointerType; 
}; 

template <class Type> struct make_pointer<Type &> 
{ 
    typedef Type *PointerType; 
}; 

/// overload for reference 
template <class Type, class SourceType> 
Type static_cast_checked(SourceType &item) 
{ 
    Assert(dynamic_cast<typename make_pointer<Type>::PointerType>(&item)); 
    return static_cast<Type>(item); 
} 

Ensuite, vous pouvez utiliser votre syntaxe désirée:

Derived *d= static_cast_checked<Derived *>(b); 
Derived &d= static_cast_checked<Derived &>(b); 

EDIT: Ajout d'une conversion de type pointeur.

+1

Vous ne pouvez pas obtenir le type de pointeur sur une référence ('Assert (dynamic_cast (& item))') au moins si vous utilisez le gcc - je ne suis pas sûr de l'autre compilateur. – nutario

+0

@nutario, ah, vous avez raison monsieur. – MSN

+0

@nutario, corrigé! – MSN

0

les opérations suivantes:

template<class Type, class SourceType> 
Type* static_cast_checked(SourceType* item) 
{ 
    // ... 
} 

template<class Type, class SourceType> 
Type& static_cast_checked(SourceType& item) 
{ 
    // ... 
} 

Ensuite, chaque pointeur utiliserait la première variante et chaque référence utilisera la deuxième aux règles de spécialisation partielle (IIRC).

2

Cela fonctionne:

/// perform a static_cast asserted by a dynamic_cast 
template <class Type, class SourceType> 
Type* static_cast_checked(SourceType *item) 
{ 
    Assert(!item || dynamic_cast<Type*>(item)); 
    return static_cast<Type*>(item); 
} 

/// overload for reference 
template <class Type, class SourceType> 
Type &static_cast_checked(SourceType &item) 
{ 
    Assert(dynamic_cast<Type *>(&item)); 
    return static_cast<Type&>(item); 
} 

utiliser comme ceci:

Dervied d; 
Base* pbase = static_cast_checked<Base>(&d); 
Base& rbase = static_cast_checked<Base>(d); 

Cette solution repose sur la surcharge des modèles de fonction. Votre solution n'a pas fonctionné car votre premier modèle est trop général, il inclut déjà votre deuxième fonction. Notez qu'il n'y a pas de spécialisation pour les modèles de fonction! Vous pouvez uniquement spécialiser les modèles de classe.

+0

Super, c'est facile et ça marche plutôt bien. Si possible, je préférerais une solution qui me permettrait quand même d'écrire static_cast_checked ou static_cast_checked . Quelqu'un peut-il en créer?J'essaie maintenant d'utiliser une classe de modèles spécialisés pour faire le travail, ce qui semble prometteur. – Suma

+0

J'ai essayé plusieurs tentatives (y compris les traits de caractères et les spécialisations de classe) et je n'ai pas trouvé de solution de compilation. – WolfgangA

+0

Je pense avoir trouvé une solution, avec une combinaison de surcharges et de spécialisations de classe. Pouvez-vous s'il vous plaît vérifier ma nouvelle réponse, pour voir si c'est OK? – Suma

1
#include <boost/type_traits/add_pointer.hpp> 

template <class Type, class SourceType> 
Type static_cast_checked(SourceType *item) 
{ 
    assert(!item || dynamic_cast<Type>(item)); 
    return static_cast<Type>(item); 
} 

template <typename Type, class SourceType> 
Type static_cast_checked(SourceType &item) 
{ 
    typedef typename boost::add_pointer<Type>::type TypePtr; 
    assert(dynamic_cast<TypePtr>(&item)); 
    return static_cast<Type>(item); 
} 

Cette implémentation fonctionne comme static_cast ou dynamic_cast du std-lib: ``

Base &b = static_cast_checked<Base&>(a); 
    Base* bPtr = static_cast_checked<Base*>(&a); 
+0

Nice. Il fait presque la même chose que ma solution et le contexte technique est le même, mais en utilisant boost il peut être un peu plus court. – Suma