У меня есть шаблон класса С++ для представления двухмерных полей с реальными и комплексными значениями. Я хотел бы перегрузить оператор присваивания, чтобы добиться глубокого копирования данных из одного поля в другое. На данный момент я ограничил данные либо double
, либо std::complex<double>
. Это означает, что необходимо рассмотреть 4 разных случая: double
-к-double
, double
-к-std::complex<double>
, std::complex<double>
-к-double
и std::complex<double>
-к-std::complex<double>
. Я хочу обработать случай std::complex<double>
-to-double
, взяв действительную часть комплексного значения; для других случаев это просто тривиальное задание. Однако я изо всех сил пытаюсь получить код для компиляции. Ниже приведена простая фиктивная версия, в которой фиксируются проблемы:
#include <complex>
template<class T>
class Number {
private:
// should make Number<T>.m_value visible to Number<U>
template<class U>
friend class Number;
T m_value;
public:
Number(const T value) : m_value{value} {
// restricting the data
static_assert(
std::is_same<T, double>::value || std::is_same<T, std::complex<double>>::value,
"Error: Number::Number: Only 'double' and 'std::complex<double>' are supported currently!"
);
// no copying allowed
Number(const Number& orig) = delete;
};
// general case
template<class U>
Number<T>& operator=(const Number<U>& another) {
m_value = another.m_value;
return *this;
}
};
// specialization
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
m_value = std::real(another.m_value);
}
int main(int argc, char** argv) {
const std::complex<double> I{0.0, 1.0};
Number<double> n{0.0};
Number<std::complex<double>> m{1.0 + I*2.0};
n = m;
return 0;
}
Вывод компилятора:
g++ -c -g -std=c++14 -MMD -MP -MF "build/Debug/GNU-Linux/main.o.d" -o build/Debug/GNU-Linux/main.o main.cpp
main.cpp:28:28: error: template-id ‘operator=<>’ for ‘Number<double>& Number<double>::operator=(const Number<std::complex<double> >&)’ does not match any template declaration
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp: In instantiation of ‘Number<T>& Number<T>::operator=(const Number<U>&) [with U = std::complex<double>; T = double]’:
main.cpp:36:5: required from here
main.cpp:23:15: error: cannot convert ‘const std::complex<double>’ to ‘double’ in assignment
m_value = another.m_value;
^
Я не могу понять, как реализовать перегрузку присваивания. Я пытался найти решение своей проблемы (например, из «Похожих вопросов») и наткнулся на множество полезных вопросов и ответов, а также включил многие вещи из них в свой код. Однако я не нашел решения своей конкретной проблемы и, похоже, застрял. Какие-либо предложения? Спасибо!
Простой способ избежать проблемы в этом случае — всегда брать std::real
. Это функция идентификации для несложных типов.
@ user17732522, но я не могу взять std::real
при назначении std::complex<double>
на std::complex<double>
- я не хочу терять мнимую часть в этом случае.
@PaulMcKenzie, возможно, ты прав. Но столкнусь ли я с теми же ошибками компиляции, если попытаюсь реализовать общую функцию и специализированную функцию для копирования данных?
@PetriHirvonen При присваивании между одинаковыми типами будет использоваться неявно определенный оператор присваивания копирования. Он вообще не будет использовать ваш оператор присваивания шаблона. Кстати, очень странно, что тип может быть копируемым, но не копируемым.
Кроме того, объявление Number(const Number& orig) = delete;
неуместно в неправильном контексте вашего вопроса.
@PetriHirvonen -- К последнему замечанию: если программист может сделать это: Foo f1; Foo f2; f2 = f1;
, то он ожидает, что это также доступно: Foo f1; Foo f2 = f1;
@user17732522 и @PaulMcKenzie, спасибо за комментарии! Я просто хотел создать поля, с которыми я мог бы выполнять некоторые математические операции, используя такие операторы, как +=
, -=
, *=
, /=
и т. д., но также хотел предотвратить создание новых из математических операций, потому что поля могут быть большими. Я думал, что =
послужит хорошим сокращением для перезаписи одного поля данными другого поля того же размера, но я откажусь от этой идеи, поскольку она кажется проблематичной.
На самом деле он говорит то, что ожидает в строке:
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
Вам нужен один «template<>», чтобы специализироваться T
на шаблоне класса, и еще один, чтобы специализироваться U
на шаблоне функции-члена:
template<>
template<>
Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) { ... }
Спасибо за ответ @dewaffled, это сработало! Компилятор часто может очень прямо сказать, что он хочет, но это другая история, понимаю ли я... Я определенно не догадывался, что это то, что нужно.
Это означает, что необходимо рассмотреть 4 различных случая: double-to-double, double-to-std::complex<double>, std::complex<double>-to-double и std::complex<double>-to-. std::complex<double> -- Звучит как рай для ошибок. Оператор присваивания также будет использоваться компилятором, возможно, даже без вашего ведома. Вот почему такие функции, как
to_whatever
, выполняют преобразование одного типа в другой, а не полагаются на операторы присваивания и операторы приведения.