4

我正在寻找一个函数,它返回对 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);

那不干净。

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

4

6 回答 6

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;
}

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

于 2013-10-04T08:35:10.947 回答
4

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

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布局保证始终有效。

于 2013-10-04T09:14:31.263 回答
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]

于 2013-10-04T10:49:13.923 回答
2

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

于 2013-10-04T08:14:43.303 回答
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,而不是未修改。)


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

于 2013-10-04T07:54:17.080 回答
-2

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

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

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

于 2013-10-04T10:00:31.377 回答