1
votes

Overloading Operator - ne peut pas convertir l'objet en type de base dans l'affectation

Je travaille sur une implémentation des nombres complexes. La classe, Complex , a deux membres privés, real_part et imaginary_part . Je voudrais remplacer l'opération de multiplication comme suit:

   In file included from main.cpp:2:
complex.hpp: In instantiation of ‘Complex operator*(T, D) [with T = Complex; D = Complex]’:
main.cpp:13:17:   required from here
complex.hpp:43:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
         real_a = lhs;
         ~~~~~~~^~~~~
complex.hpp:53:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
         real_b = rhs;
         ~~~~~~~^~~~~
complex.hpp: In instantiation of ‘Complex operator*(T, D) [with T = Complex; D = int]’:
main.cpp:14:20:   required from here
complex.hpp:43:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
         real_a = lhs;
         ~~~~~~~^~~~~
complex.hpp:48:22: error: request for member ‘real_part’ in ‘rhs’, which is of non-class type ‘int’
         real_b = rhs.real_part;
                  ~~~~^~~~~~~~~
complex.hpp:49:27: error: request for member ‘imaginary_part’ in ‘rhs’, which is of non-class type ‘int’
         imaginary_b = rhs.imaginary_part;
                       ~~~~^~~~~~~~~~~~~~

Mes constructeurs ressemblent à:

  Complex a(4.0, 8.0);
  Complex b(8, 16);
  auto prod = a*b;
  auto prod2 = a * 2;

et:

  Complex(T real, T imaginary)
  {
    real_part = real;
    imaginary_part = imaginary;
  }

Lorsque j'essaye de multiplier deux complexes ensemble:

Complex::Complex()
{
  real_part = 0.0;
  imaginary_part = 0.0;
}

Je reçois l'erreur suivante:

  template<typename T, typename D>
  friend Complex operator * (T lhs, D rhs)
  {
    double real_a;
    double real_b;
    double imaginary_a;
    double imaginary_b;

    if(std::is_same<T, Complex>::value)//if lhs is a Complex
      {
        real_a = lhs.real_part;
        imaginary_a = lhs.imaginary_part;
      }
    else //base type, some sort of number
      {
        real_a = lhs;
        imaginary_a = 0;
      }
    if(std::is_same<D, Complex>::value)//if rhs is a Complex
      {
        real_b = rhs.real_part;
        imaginary_b = rhs.imaginary_part;
      }
    else //base type, some sort of number
      {
        real_b = rhs;
        imaginary_b = 0;
      }
    Complex result;
    result.real_part = (real_b*real_a- imaginary_b*imaginary_a);
    result.imaginary_part = (real_b*imaginary_a + imaginary_b*real_a);
    return result;
  }


3 commentaires

Ajoutez également vos constructeurs Complex . Et la version de C ++ que vous utilisez?


Salut @ P.W, j'ai ajouté les constructeurs. J'utilise C ++ 11. J'ai réalisé que mon exemple manquait un peu; Je voudrais pouvoir multiplier deux Complex s ensemble ainsi que par ex. un int et un Complex .


J'ai demandé la version car en C ++ 17, vous pouvez utiliser constexpr pour éliminer les branches incorrectes. En C ++ 11, vous devez utiliser la spécialisation. Je voulais poster une réponse mais quelqu'un m'a battu. :)


3 Réponses :


1
votes

Ceci est dû au fait que les deux branches du if sont compilées pour la même instatiation. Certaines de ces instanciations sont invalides.

Si vous pouvez travailler avec c ++ 17, vous pouvez remplacer if par if constexr , de cette façon seule la branche correcte est instanciée:

XXX

Pour le C ++ 11 strict, vous pouvez utiliser la surcharge de fonctions pour distinguer les deux types:

  template<typename T>
  static  T  getReal(T x)
  {
      return x;
  }
  static  double  getReal(Complex x)
  {
      return x.real_part;
  }
  template<typename T>
  static  T  getImaginary(T x)
  {
      return 0;
  }
  static  double  getImaginary(Complex x)
  {
      return x.imaginary_part;
  }

  template<typename T, typename D>
  friend Complex operator * (T lhs, D rhs)
  {
    double real_a = getReal(lhs);
    double real_b = getReal(rhs);
    double imaginary_a = getImaginary(lhs);
    double imaginary_b = getImaginary(rhs);

    Complex result;
    result.real_part = (real_b*real_a- imaginary_b*imaginary_a);
    result.imaginary_part = (real_b*imaginary_a + imaginary_b*real_a);
    return result;
  }


0 commentaires

1
votes

Une instanciation d'un modèle doit vérifier le type dans son intégralité.

Bien que vous puissiez utiliser if constexpr pour sélectionner une branche au moment de la compilation, je pense que c'est en fait plus simple l'ancien- façon façonnée, avec surcharge et sans condition:

// Mutating multiplication as a member
template<typename T>
Complex<T>& Complex<T>::operator*=(const Complex<T>& rhs)
{
    auto real = real_part;
    auto imaginary = imaginary_part;
    real_part = real * rhs.real_part - imaginary * rhs.imaginary_part;
    imaginary_part = imaginary * rhs.real_part + real * rhs.imaginary_part;
    return *this;
}

// These are free non-friend functions.
template<typename T>
Complex<T> operator*(Complex<T> lhs, const Complex<T>& rhs)
{
    return lhs *= rhs;
}

template<typename T>
Complex<T> operator*(const Complex<T>& lhs, T rhs)
{
    return lhs * Complex(rhs, 0);
}

template<typename T>
Complex<T> operator*(T lhs, const Complex<T>& rhs)
{
    return rhs * lhs;
}


0 commentaires

1
votes

Je suis d'accord avec la réponse de Michael Veksler , ie , la règle de base est que le modèle fonctionne sera compilé en plusieurs instances selon la façon dont vous avez appelé la fonction. La compilation sera faite pour tout le code tandis que l'assertion de std :: is_same est à l'exécution. Par conséquent, la compilation ne passera pas car certaines branches de votre code ne sont pas valides pour des types d'arguments spécifiques (par exemple int pour l'instruction real_b = rhs.real_part ).

À simplifier le code, nous pouvons définir un autre constructeur pour le type Complex en transposant d'abord des types numériques simples ( eg , int ou double ) en type Complex .

static Complex operator * (const Complex & lhs, const Complex & rhs)
{
    Complex result(lhs.real_part*rhs.real_part - lhs.imaginary_part * rhs.imaginary_part,
        lhs.real_part*rhs.imaginary_part + lhs.imaginary_part * rhs.real_part);
    return result;
}

Ensuite, nous pouvons modifier la fonction de surcharge de l'opérateur * comme p >

template<typename T>
Complex(T real, T imaginary = 0)
{
    real_part = real;
    imaginary_part = imaginary;
}

NOTEZ que: les arguments d'entrée seront automatiquement convertis dans le type Complex par le constructeur nouvellement défini ou le constructeur de copie. De plus, le résultat renvoyé est construit avec le nouveau constructeur.


0 commentaires