Question

Type de question aléatoire ...

Ce que je recherche, c’est un moyen d’exprimer une opération de conversion qui utilise un opérateur défini de l’instance de la classe à partir de laquelle je le lance et génère une erreur de compilation s’il n’existe pas d’opérateur de conversion défini pour le type. . Ainsi, par exemple, ce que je recherche, c’est quelque chose comme:

template< typename RESULT_TYPE, typename INPUT_TYPE >
RESULT_TYPE operator_cast( const INPUT_TYPE& tValue )
{
    return tValue.operator RESULT_TYPE();
}

// Should work...
CString sString;
LPCTSTR pcszString = operator_cast< LPCTSTR >( sString );

// Should fail...
int iValue = 42;
DWORD dwValue = operator_cast< DWORD >( iValue );

Note complémentaire intéressante: le code ci-dessus bloque le compilateur VS2005 C ++ et ne se compile pas correctement dans le compilateur VS2008 en raison de ce que je devine être un bogue du compilateur, mais en illustre l'idée, espérons-le.

Quelqu'un connaît-il un moyen quelconque d'obtenir cet effet?

Edit: Plus de justification, pour expliquer pourquoi vous pourriez utiliser ceci. Supposons que vous ayez une classe wrapper qui est supposée encapsuler ou résumer un type et que vous la transposez dans le type encapsulé. Vous pouvez utiliser static_cast & Lt; & Gt ;, mais cela pourrait fonctionner si vous vouliez que cela échoue (c.-à-d. Si le compilateur choisit un opérateur autorisé à convertir le type demandé, lorsque vous souhaitiez un échec. parce que cet opérateur n'est pas présent).

Certes, c’est un cas peu commun, mais c’est ennuyeux de ne pouvoir exprimer exactement ce que je veux que le compilateur fasse dans une fonction encapsulée ... d’où la question qui se pose ici.

Était-ce utile?

La solution

Le code que vous avez publié fonctionne avec le compilateur Cameau (ce qui indique généralement valide C ++).

Comme vous le savez, une distribution valide ne comprend pas plus d'une distribution définie par l'utilisateur, une solution à laquelle je pensais par conséquent consistait à ajouter une autre distribution définie par l'utilisateur en définissant un nouveau type dans le modèle de diffusion et en ayant affirmation statique qu'aucune distribution n'est disponible du nouveau type au type de résultat (à l'aide de boost is_convertible ), ce qui ne distingue pas entre les opérateurs de distribution et les constructeurs de distribution (ctor avec un argument) et permet à des conversions supplémentaires de se produire (par exemple, void* à bool). Je ne sais pas si la bonne solution est de distinguer les opérateurs et les constructeurs de la distribution, mais c'est ce que dit la question.

Après quelques jours de réflexion, vous pouvez simplement prendre l’adresse de l’opérateur de la distribution. C’est un peu plus facile à dire qu’à faire en raison du pointeur poilu de C ++ sur la syntaxe des membres (il m’a fallu beaucoup plus de temps que prévu pour bien le comprendre). Je ne sais pas si cela fonctionne sur VS2008, je ne l'ai vérifié que sur Cameau.

template< typename Res, typename T>
Res operator_cast( const T& t )
{
    typedef Res (T::*cast_op_t)() const;
    cast_op_t cast_op = &T::operator Res;
    return (t.*cast_op)();
}

Modifier: J'ai eu la chance de le tester sur VS2005 et VS2008. Mes résultats diffèrent de ceux de l'affiche originale.

  • Sur VS2008, la version originale semble bien fonctionner (comme la mienne).
  • Sur VS2005, la version d'origine ne bloque le compilateur que lors de la conversion d'un type intégré (par exemple, casting int à int) après avoir fourni une erreur de compilation qui ne semble pas trop grave, et ma version semble fonctionner dans tous les cas.

Autres conseils

L'utilisation d'un constructeur de conversion marqué est explicite , voici comment empêcherait le compilateur d’autoriser les types convertis implicitement à initialiser votre classe wrapper.

Comme les messages d’erreur du compilateur liés aux modèles sont une tâche ardue, si vous n’avez pas l’esprit de spécifier chaque conversion, vous pouvez demander au compilateur d’émettre un message plus instructif en cas d’échec en fournissant également une définition de modèle par défaut. Ceci utilise le fait que le compilateur ne tentera de compiler que du code dans les modèles réellement invoqués.

#include <string>

// Class to trigger compiler warning   
class NO_OPERATOR_CONVERSION_AVAILABLE
{
private:
   NO_OPERATOR_CONVERSION_AVAILABLE(){};
};

// Default template definition to cause compiler error
template<typename T1, typename T2> T1 operator_cast(const T2&)
{
   NO_OPERATOR_CONVERSION_AVAILABLE a;
   return T1();
}

// Template specialisation
template<> std::string operator_cast(const std::string &x)
{
   return x;
}

sonne comme vous voulez une spécialisation de gabarit, comme ceci:

/* general template */
template<typename T1, typename T2> T1 operator_cast(const T2 &x);

/* do this for each valid cast */
template<> LPCTSTR operator_cast(const CString &x) { return (LPCTSTR)x; }

EDIT: comme indiqué dans un autre article, vous pouvez ajouter quelque chose dans la version générale pour vous donner un message d'erreur plus utile si une distribution non prise en charge est effectuée.

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