2 votes

La surcharge de l'opérateur de conversion c++ échoue

J'ai du code pour surcharger l'opérateur de conversion pour différents types :

#include <string>
#include <iostream>

class MyClass {
   int m_int;
   double m_double;
   std::string m_string;

public:
   MyClass() : m_int{1}, m_double(1.2), m_string{"Test"} {};

   // overload the conversion operator for std::string
   operator std::string() {
      return m_string;
   }

   // overload the conversion operator for all other types
   template <typename T>
   operator T() {
      if (std::is_same<T, int>::value)
         return m_int;
      else
         return m_double;
   }
};

int main() {

   MyClass o;

   int i = o;
   double d = o;
   std::string s = o;

   std::cout << i << " " << " " << d << " " << s << std::endl;
}

Cela fonctionne correctement. Mais lorsque j'essaie de faire

std::string s;
s = o;

la compilation échoue avec

error: use of overloaded operator '=' is ambiguous (with operand types 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') and 'MyClass')

Apparemment, le compilateur instancie un opérateur de conversion différent de std::string(). Si je supprime la section template, le compilateur est forcé d'utiliser l'opérateur de conversion std::string() et cela fonctionne à nouveau. Qu'est-ce qui m'échappe ?

3voto

hsalimi Points 3712

En std::string il est possible d'utiliser operator=() pour assigner des caractères (et par conséquent des entiers) à l'élément std::string . Cela signifie que le code suivant est acceptable parce que l'élément operator=(char) existe :

std::string s1;

s1 = '1'; // works fine
s1 = 24; // works fine

Mais vous ne pouvez pas copier une chaîne de caractères en utilisant des caractères (et donc des entiers).

std::string s2  = '1'; // compilation fails
std::string s3 = 24;   // compilation fails

Ainsi, dans votre cas, lorsque vous utilisez la construction par copie, il n'y a pas d'ambiguïté, car std::string ne peut pas être copié en utilisant un double ou un int. Mais pour les operator= il est ambigu parce que vous pouvez assigner des entiers dans une chaîne et le compilateur ne sait pas quel opérateur de conversion utiliser. En d'autres termes, le compilateur peut utiliser à la fois vos opérateurs de conversion string et int pour cela.

0voto

Stefan Points 41

J'ai réussi à le faire fonctionner (grâce à mon collègue NB) :

#include <string>
#include <iostream>

class MyClass {
   int m_int;
   double m_double;
   std::string m_string;

public:
   MyClass() : m_int{1}, m_double(1.2), m_string{"Test"} {};

   // overload the conversion operator for std::string
   operator std::string() {
      return m_string;
   }

   // overload the conversion operator for all other types
   template <typename T, typename std::enable_if<
           std::is_same<T, int>::value ||
           std::is_same<T, double>::value
           ,T>::type* = nullptr>
   operator T() {
      if (std::is_same<T, int>::value)
         return m_int;
      else
         return m_double;
   }
};

int main() {

   MyClass o;

   int i = o;
   double d = o;
   std::string s;
   s = o;

   std::cout << i << " " << " " << d << " " << s << std::endl;
}

Il faut donc activer le modèle pour tous les types valides. Mais dès que (char) figure dans la liste des types sous std::enable_if<>, l'erreur se reproduit. Apparemment, l'opérateur std::string= veut faire correspondre à char (Voir point 4 ci-dessus).

Ce que je ne comprends pas pour l'instant, c'est pourquoi le fait de spécifier explicitement les opérateurs de conversion int() et double() a pour effet d'augmenter le nombre d'opérations de conversion. PAS travail :

operator int() {
   return m_int;
}

operator double() {
   return m_double;
}

alors que cela devrait être équivalent à

   template <typename T, typename std::enable_if<
           std::is_same<T, int>::value ||
           std::is_same<T, double>::value
           ,T>::type* = nullptr>
   operator T() {
      if (std::is_same<T, int>::value)
         return m_int;
      else
         return m_double;
   }

La raison pour laquelle cela ne fonctionne pas n'est pas claire non plus :

template <typename T, typename std::enable_if<
        std::is_same<T, int>::value ||
        std::is_same<T, double>::value ||
        std::is_same<T, std::string>::value
        ,T>::type* = nullptr>
operator T() {
   if (std::is_same<T, std::string>::value)
      return m_string;
   else if (std::is_same<T, int>::value)
      return m_int;
   else
      return m_double;
}

Peut-être que quelqu'un peut me donner un aperçu.

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