在C ++ 11中返回对复数的实数或图像值的引用的函数

时间:2013-10-04 07:35:44

标签: c++ gcc c++11 complex-numbers

我正在寻找一个函数,它返回对C ++ 11中复数的实数或图像值的引用。在C ++ 03中我可以说:

complex<double> C; cin >> C.real();

但是在C ++ 11中,由于C.real()返回的值不是引用,因此给出了编译错误。

我发现我可以这样写:

double t; cin >> t; C.real(t);

但这并不简单,例如,如果我想将c的实部乘以2并将其加1,我应该说:

C.real(2*C.real() + 1);

那不干净。

还有其他[干净]方法吗?

6 个答案:

答案 0 :(得分:5)

如果你真的想要为复杂的实部和虚部分开输入,你可以试试IO操纵器方法。

#include <complex>
#include <iosfwd>

class proxy_complex {
    explicit proxy_complex(std::istream& strm, bool f)
        : strm_(&strm), flag(f) { }
    proxy_complex(const proxy_complex&) = default;

    std::istream* strm_;
    bool flag;           // flag to check whether we're writing real or imag

public:
    template<typename T>
    std::istream& operator>>(std::complex<T>& c)
    {
        T n;
        if (*strm_ >> n)
            flag ? c.real(n) : c.imag(n);
        return *strm_;
    }

    friend proxy_complex operator>>(std::istream& is, proxy_complex(*func)(std::istream&))
    {
        return func(is);
    }
    friend proxy_complex real(std::istream&);
    friend proxy_complex imag(std::istream&);
};

inline proxy_complex real(std::istream& is)
{
    return proxy_complex(is, true);
}

inline proxy_complex imag(std::istream& is)
{
    return proxy_complex(is, false);
}

您可以将上面的代码放在自己的头文件中(如果这样做,将它包装在命名空间中可能是个好主意。)

用法:

#include <iostream>
#include "my_header.h"

int main()
{
    std::complex<double> c;
    std::cin >> real >> c >> imag >> c;
    if (std::cin) std::cout << c;
}

希望我猜错了你对“干净”的定义:)

答案 1 :(得分:4)

很抱歉是否定的,但你的问题是从一个错误的前提开始的。关于std::complex 2011标准是向后兼容的。表格代码

complex<double> C; cin >> C.real();

永远不会有效的C ++ 。 2003标准仅给出了成员函数

T std::complext<T>::real() const;

但不是

const T& std::complext<T>::real() const;  // non-standard
T& std::complext<T>::real();              // non-standard

即使某些实现(例如gcc 4.3附带的实现)可能已经实现了这两个实现。

现在,回答您的问题。显然,最简洁的方法是遵循标准的意图。 2011标准增加了以下的设定者

void std::complex<T>::real(T);
void std::complex<T>::imag(T);

因此您现在可以简单地使用它们分别设置实部或虚部。

但是,这些不能用于T&的功能,例如operator>>。为此,你必须做一些讨厌的技巧,我不能真正推荐:

template<typename T>
T& get_real(std::complex<T>&z)   // dirty cast to gain non-const access to member.
{
  struct Z { T x,y; };           // assumes that std::complex<> has same layout
  static_assert(sizeof(Z)==sizeof(std::complex<T>),"!!");
  static_assert(alignof(Z)==alignof(std::complex<T>),"!!");
  return static_cast<Z&>(z).x;
}

std::complex<double> z;
cin >> get_real(z);

但是,这超出标准并且无法保证正常工作,因为标准没有对std::complex<>的布局做出规定:它可以以相反的顺序存储其数据成员任何其他方式。

答案 2 :(得分:3)

C ++ 11现在允许

double& re(std::complex<double>& c)
{
    return reinterpret_cast<double (&)[2]>(c)[0];
}

double& im(std::complex<double>& c)
{
    return reinterpret_cast<double (&)[2]>(c)[1];
}

const double& re(const std::complex<double>& c)
{
    return reinterpret_cast<const double (&)[2]>(c)[0];
}

const double& im(const std::complex<double>& c)
{
    return reinterpret_cast<const double (&)[2]>(c)[1];
}

用法:

std::complex<double> a;
std::cin >> re(a);

相关引用§26.4:

  

此外,如果a是cv std::complex<T>*类型的表达式,并且表达式a[i]已为整数表达式i定义良好,则:     - reinterpret_cast<cv T*>(a)[2*i]应指定a[i]的真实部分,并且    - reinterpret_cast<cv T*>(a)[2*i+1]应指定a[i]的虚部。

答案 3 :(得分:2)

如果你想操纵实际零件,你可以直接使用double或float。 如果你想操纵虚部,你可以得到一个唯一的复数std::complex<double> I(0,1),然后乘以你想要的值。

例如,您可以写下:C.real(2*C.real() + 1);

,而不是写C += C.real() + 1;

然后你可以在数学表达式中将双打与复数混合,编译器将使用正确的转换。查看示例:

#include <iostream>
#include <complex>

int main(int argc, char* argv[])
{
    // Let the user enter a Real number
    double c;
    std::cin >> c;

    // Explicitly convert to a complex
    std::complex<double> C = 2*c + 1;
    std::cout << C << std::endl;

    // Creates a pure imaginary complex number I
    std::complex<double> I(0,1);

    // Mix together complex and real numbers in the
    // same expression
    C = C + c*I;
    std::cout << C << std::endl;


    // Setup a specific value and compare how to achieve
    // C.real = 2*C.real + 1
    C = 1. + 2.*I;
    C.real(2*C.real()+1);
    std::complex<double> D = 1. + 2.*I;
    D += D.real() + 1;
    std::cout << "C=" << C << "\tD=" << D << std::endl;

    return 0;
}

输出:

$ ./main.exe
1
(3,0)
(3,1)
C=(3,2) D=(3,2)

$ ./main.exe
2
(5,0)
(5,2)
C=(3,2) D=(3,2)

如果您害怕此方法的效率损失与直接通过引用影响相比,您可以查看生成的汇编代码。在g++-O3的计算机上,所有内容都已内联。

答案 4 :(得分:1)

不是我知道的。

如果这对你很重要,你可以构建一个帮助者:

class ModifyReal
{
   double d;
   complex<double> & c;
public:
   ModifyReal(complex<double> & c_) : c(c_), d(numeric_limits<double>::quiet_NaN()) 
   {}
   operator double &() { return d; }
   ~ModifyReal() { c.real(d); }
};


cin >> ModifyReal(C);

我不会推荐使用它,但是,除非你有非常令人信服的理由。 (“我不喜欢它”并不足够令人信服。)

我认为在你的代码中有许多不同的类可能会妨碍可读性,但如果你在一些专用的实例中使用它,你应该没问题。错误处理可能变得微妙困难(例如,因为cin不会抛出无效输入,C被指定为nan,而不是未经修改。)


“干净”是什么意思?不,不要告诉我 - 想一想。

答案 5 :(得分:-2)

受史蒂夫杰西普的启发,它只是C += (C + C.conj())/2 + 1;

请记住,在复杂的数学中,你不能真正将实部和虚部视为完全独立的部分。这与将其相位和幅度视为完全独立的组件一样理智。复数的加法是在实部和虚部上独立完成的,但乘法是在相位和幅度部分上独立完成的。

您的示例不是复数乘法,因此std::complex不支持这种乘法是有道理的。