Функция, возвращающая ссылку на действительное или воображаемое значение комплексного числа в C++11

Я ищу функцию, которая возвращает ссылку на действительные или imag значения комплексного числа в 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 ответов

Решение

Извините за негатив, но ваш вопрос начинается с неверной предпосылки. Что касается 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<>: он может хранить свои элементы данных в обратном порядке или любым другим способом.

Если вы действительно хотите разделить ввод для реальной и мнимой частей комплекса, вы можете попробовать подход манипуляторов ввода-вывода.

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

Надеюсь, я угадал ваше определение "чистый" правильно:)

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],

Если вы хотите манипулировать реальными частями, вы можете просто использовать 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 все встроено.

Не то, что я знаю из.

Вы можете создать помощника, если это важно для вас:

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 не поддерживает такого рода умножение.

Другие вопросы по тегам