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