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 Réponses :
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; }
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; }
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.
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. unint
et unComplex
.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. :)