преобразовать int в double для `*` операций с std :: complex <double> - PullRequest
0 голосов
/ 09 июля 2019

Я физик, пытающийся свести к минимуму типизацию преобразований типов и приведений в файле кода, предназначенном для вычислений (уравнения и функции). В расчетах обычно используются комплексные числа. Поэтому я расширил тип complex<double> как cd для компактности и добавил несколько вспомогательных методов.

class cd: public complex<double> { ... }

Причина расширения, а не просто использования typedef, заключается в том, что физический символ (string) и физические единицы (string) могут храниться вместе с физической переменной.

Теперь, если в вычислениях у меня есть такие примеры, как

int i = 2;
cd z(1,2);
cout << i*z;

Это дает ошибку, так как нет оператора, который умножает int и cd. (Я думал, что c ++ автоматически неявно преобразует int в double и использует соответствующий оператор.) После определения такого оператора вручную как такового

cd operator*(const int& i, const cd& z)
{
  return cd(i*z.real(),i*z.imag());
}

c ++ затем предупреждает о неоднозначности преобразования типов для таких частей, как

double x = 30;
x*z;

В следующем x является двойным, а I является cd.

 error: ambiguous overload for ‘operator*’ (operand types are ‘double’ and ‘const cd’)
   return pow(eps1/e0*kz2,2)-pow(eps2/e0*kz1*tanh(dist*1e-10*kz1/( x *I)),2);
                                                                  ~~~^~
In file included from libs/calc/include/calculation.h:12:0,
                 from scripts/dist_dependence.cpp:2:
libs/calc/include/complex_double.h:49:4: note: candidate: cd operator*(const int&, const cd&)
 cd operator*(const int& x, const cd& z)

Поскольку определение оператора (см. Выше) также можно использовать для double с cd - что уже определено в стандартной библиотеке.

Теперь вышеуказанную проблему можно решить, определив вместо нее

cd operator*(const double& x, const cd& z)
{
  return cd(x*z.real(),x*z.imag());
}

Однако это предотвращает следующее:

В дополнение к этому, мне также хотелось бы преобразовать cd в double, чтобы комплексные числа можно было передавать (без необходимости явного преобразования) в функции, принимающие реальные (типа double) аргументы. (преобразовать cd в double, если мнимая часть равна нулю, в противном случае выведите ошибку или что-то в этом роде). Проблема в том, что когда я определяю (в дополнение к double - cd оператор *:

operator double() {
    if (imag()==0.0) return real();
    throw "trying to cast a cd with non-zero imaginary part to double";
  }

внутри cd класса.

Выплевывает следующее:

 warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:

Например, здесь дано только operator*, но я бы хотел сделать это и для других математических двоичных операций.

Ответы [ 2 ]

3 голосов
/ 09 июля 2019

Ваша проблема здесь:

Так что я расширил тип complex<double> как cd

Это распространенная ошибка новичков, предполагающих, что наследование является ответом на все проблемы, хотя на самом деле оно является источником многих проблем.

Просто определите рабочий тип без наследования, и все будет работать из коробки:

using cd = std::complex<double>;
constexpr cd i{0 , 1};

int main (int , char **)
{
    cd x{ 1, 3};

    std::cout << x << '\n';
    std::cout << x*i << '\n';

    std::cout << x*i + 3.2 << '\n';

    return 0;
}

https://wandbox.org/permlink/OfOfonJFrTInR0ib

Отказ от ответственности: cd не лучшее название для этого символа. Подумайте о чем-нибудь более информативном

1 голос
/ 10 июля 2019

Я создал минимальный пример, который, как мне кажется, демонстрирует ваши проблемы.Надеюсь, это также иллюстрирует комментаторам, что вы пытаетесь достичь.

#include <iostream>
#include <complex>
#include <string>

class cd: public std::complex<double> {
  public:
    cd(double re, double im):std::complex<double>(re,im),name("var1"){}
    operator double(){
      if (imag()==0.0) return real();
      throw "trying to cast a cd with non-zero imaginary part to double";
    }
    friend std::ostream& operator<<(std::ostream& os, const cd& z){
      os << z.name << "=(" << z.real() << "," << z.imag() << ")";
      return os;
    }

  private:
    std::string name;

};

cd operator*(const int& i, const cd& z){
  return cd(i*z.real(),i*z.imag());
}

cd operator*(const double& x, const cd& z){
  return cd(x*z.real(),x*z.imag());
}

void foo(double x){
  std::cout << "foo " << x << std::endl;
}


int main(){

  int i=2;
  cd z(1,2);
  std::cout << i*z << std::endl;

  double x=30;
  std::cout << x*z << std::endl;

  cd zz(3,0);

  foo(x*zz);

  std::cout << z*zz << std::endl;
}

, который дает следующий вывод из g++ (версия 7.4.0)

test_complex_double.cc: In function ‘int main()’:
test_complex_double.cc:48:18: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   std::cout << z*zz << std::endl;
                  ^~
In file included from test_complex_double.cc:2:0:
/usr/include/c++/7/complex:386:5: note: candidate 1: std::complex<_Tp> std::operator*(const std::complex<_Tp>&, const std::complex<_Tp>&) [with _Tp = double]
     operator*(const complex<_Tp>& __x, const complex<_Tp>& __y)
     ^~~~~~~~
test_complex_double.cc:22:4: note: candidate 2: cd operator*(const int&, const cd&)
 cd operator*(const int& i, const cd& z){
    ^~~~~~~~
test_complex_double.cc:48:18: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   std::cout << z*zz << std::endl;
                  ^~
In file included from test_complex_double.cc:2:0:
/usr/include/c++/7/complex:386:5: note: candidate 1: std::complex<_Tp> std::operator*(const std::complex<_Tp>&, const std::complex<_Tp>&) [with _Tp = double]
     operator*(const complex<_Tp>& __x, const complex<_Tp>& __y)
     ^~~~~~~~
test_complex_double.cc:26:4: note: candidate 2: cd operator*(const double&, const cd&)
 cd operator*(const double& x, const cd& z){
    ^~~~~~~~

Это всего лишь предупреждение, и этот пример все еще компилируется.

Я думаю, что решение состоит в том, что вы хотите, чтобы ваш класс был контейнером для std::complex<double>, а не наследовался от него.Я предполагаю, что вы хотите наследовать, чтобы вам не приходилось реализовывать функции-оболочки вокруг всего, что реализует std::complex<double>, но контейнерный подход, на мой взгляд, имеет больше смысла, а также решает эту конкретную проблему.

Вот рабочий пример, показывающий альтернативу контейнера:

#include <iostream>
#include <complex>
#include <string>

class cd {
  public:
    cd(double re, double im):val(re,im),name("var1"){}
    cd(const std::complex<double>& v):val(v),name("var1"){}
    operator double(){
      if (val.imag()==0.0) return val.real();
      throw "trying to cast a cd with non-zero imaginary part to double";
    }
    friend std::ostream& operator<<(std::ostream& os, const cd& z){
      os << z.name << "=(" << z.real() << "," << z.imag() << ")";
      return os;
    }
    double real() const{return val.real();}
    double imag() const{return val.imag();}

    cd operator*(const cd& other)const{return val*other.val;}

  private:
    std::complex<double> val;
    std::string name;

};

cd operator*(const int& i, const cd& z){
  return cd(i*z.real(),i*z.imag());
}

cd operator*(const double& x, const cd& z){
  return cd(x*z.real(),x*z.imag());
}

void foo(double x){
  std::cout << "foo " << x << std::endl;
}


int main(){

  int i=2;
  cd z(1,2);
  std::cout << i*z << std::endl;

  double x=30;
  std::cout << x*z << std::endl;

  cd zz(3,0);
  foo(x*zz);

  std::cout << z*zz << std::endl;

}

Компилируется без предупреждений, и при запуске программы выводится:

var1=(2,4)
var1=(30,60)
foo 90
var1=(3,6)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...