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

最后都变了- 提交于 2019-12-05 14:36:55

Sorry to be negative, but your question starts from a wrong premise. Concerning std::complex the 2011 standard is backward compatible. Code of the form

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

was never valid C++. The 2003 standard only gives the member function

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

but not

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

even though some implementations (such as that shipped with gcc 4.3) may have implemented these two instead.

Now, to answer your question. Clearly, the cleanest way is to follow the intention of the standard. The 2011 standard adds the following setters

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

so you can now simply use those to set the real or imaginary parts separately.

However, those cannot be used in a function taking T&, such as operator>>. For that you must do some nasty tricks like

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

Actually, as pointed out in a comment by bames53, the standard guarantees std::complex to be laid out such that this always works.

If you really want to separate input for real and imaginary parts of a complex, you could try IO manipulators approach.

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

You can put the above code in a header file of its own (if you do that, it might be a good idea to wrap it in a namespace).

Usage:

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

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

Hope I guessed your definition of "clean" correctly :)

C++11 now allows

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

Usage:

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

Relevant quote §26.4:

Moreover, if a is an expression of type cv std::complex<T>* and the expression a[i] is well-defined for an integer expression i , then: — reinterpret_cast<cv T*>(a)[2*i] shall designate the real part of a[i], and — reinterpret_cast<cv T*>(a)[2*i+1] shall designate the imaginary part of a[i] .

If you want to manipulate real parts you can just use double or floats directly. If you want to manipulate imaginary parts you can have a unique complex number std::complex<double> I(0,1) and multiply it by the value you want.

For instance, instead of writing: C.real(2*C.real() + 1); you can write: C += C.real() + 1;

Then you can mix doubles with complexs in your mathematical expressions and the compiler will use the correct conversions. See an example:

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

The output:

$ ./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)

If you are afraid of the lost of efficiency of this method compared to affecting directly through a reference you can look at the generated assembly code. On my computer with g++ and -O3 everything is inlined.

Not that I know of.

You could construct a helper if that's important to you:

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

I would not exactly recommend to use this, however, unless you have a very compelling reason to. ("I do not like it" is not convincing enough.)

I do think having many different classes like this in your code can hamper readability, but if you use it in a few dedicated instances you should be fine. Error handling can become subtley difficult (e.g. since cin doesn't throw on invalid input, C gets assigned nan, rather than being unmodified.)


What does "clean" mean? No, don't tell me - think about it.

Inspired by Steve Jessop, it's just C += (C + C.conj())/2 + 1;.

Remember that in complex math, you cannot really treat the real and imaginary parts as fully independent components. That's just about as sane than treating their phase and magnitude as fully independent components. Addition of complex numbers is done independently on real and imaginary parts, but multiplication is done independently on the phase and magnitude parts.

Your example is not complex multiplication, so it makes sense that std::complex doesn't support that kind of multiplication.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!