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

a function returning reference to real or imag values of a complex number in C++11

本文关键字:IMAG 引用 函数 实数 返回 C++11      更新时间:2023-10-16

>我正在寻找一个函数,该函数返回对 C++11 中复数的实数或 imag 值的引用。在 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);

那是不干净的。

还有其他[干净]的方法可以做到这一点吗?

如果你真的想分离复合体的实部和虚部的输入,你可以尝试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;
}

希望我猜对你对"干净"的定义是正确的:)

很抱歉是消极的,但你的问题从一个错误的前提开始。关于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>
inline T& get_real(std::complex<T>&z) { return reinterpret_cast<T(&)[2]>(z)[0]; }
template<typename T>
inline T& get_imag(std::complex<T>&z) { return reinterpret_cast<T(&)[2]>(z)[1]; }
std::complex<double> z;
cin >> get_real(z) >> get_imag(z);

实际上,正如 bames53 的评论中所指出的那样,标准保证std::complex布局,以便始终有效。

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]的虚部。

如果你想操纵真实的部分,你可以直接使用双精度或浮点数。如果你想操纵虚部,你可以有一个唯一的复数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一切都是内联的。

不是我知道的。

如果这对您很重要,您可以构造一个帮助程序:

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,而不是未修改。


"干净"是什么意思?不,不要告诉我 - 想想看。

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

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

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