使用std :: complex <double>

时间:2019-07-09 13:20:13

标签: c++ c++11 physics

我是一位物理学家,试图最大程度地减少键入类型的转换和用于计算(等式和函数)的代码文件中的强制转换。计算通常涉及复数。因此,为了紧凑起见,我将complex<double>类型扩展为cd,并添加了一些帮助方法。

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

扩展而不是仅使用typedef的原因是,可以将物理符号(string)和物理单位(string)与物理变量一起存储。

现在,如果在计算中我有

这样的实例
int i = 2;
cd z(1,2);
cout << i*z;

这将导致错误,因为没有运算符可将intcd相乘。 (我想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是双精度字符,Icd

 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的{​​{1}}-标准库中已经定义了。

现在可以通过定义上述问题来解决

cd

但是,这样做可以防止以下情况:

除此之外,我还希望从cd operator*(const double& x, const cd& z) { return cd(x*z.real(),x*z.imag()); } cd的转换,以便可以将复数(不需要显式转换)传递给实数({{1 }}类型)的参数。 (如果虚部为零,则将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,但我也想对其他数学二进制运算执行此操作。

2 个答案:

答案 0 :(得分:3)

您的问题在这里:

  

所以我将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 :(得分:1)

我创建了一个最小的示例,我相信它可以演示您的问题。希望它也可以向评论者说明您要完成的工作。

#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)