Шаблоны C ++ - обобщают скалярное произведение для двойных и сложных <double>векторов - PullRequest
0 голосов
/ 16 марта 2020

Я пытаюсь перегрузить оператор * для вычисления скалярного произведения для векторов vector<double> и vector<complex<double>> с использованием шаблонов, и я пришел к этому после попытки всего, что я знаю:

#include <iostream>
#include <complex>
#include <vector>
using namespace std;

template<typename T> T operator* (const vector<T> &a, const vector<T> &b) {
    T retvar; complex<double> c = 0;
    for (int i = 0; i < b.size(); i++) c += conj(a[i])*b[i];
    if (is_same<T, double>::value) retvar = c.real();
    else                           retvar = c;  // (*) the error is generated by this line
    return retvar;
}

int main() {
    vector<double> a, b;
    a.push_back(5); b.push_back(3);
    cout << a*b << endl;
    return 0;
}

Я пытаюсь сделать следующее: я вычисляю скалярное произведение как для комплексных чисел, а затем возвращаю его, содержащийся в переменной, тип которой задается шаблоном, чтобы избежать любого странного преобразования. Тем не менее, g ++ выдает следующую ошибку в строке (*):

error: assigning to 'double' from incompatible type 'complex<double>'

Честно говоря, у меня закончились идеи, что было бы хорошим способом выбраться из этого без перегрузки оператора дважды

1 Ответ

0 голосов
/ 16 марта 2020

Не перегружайте операторы типов, которыми вы не владеете. Вместо этого вы можете написать

template <typename T>
struct my_vect {
    std::vector<T> data;
    T operator* (const my_vect<T>& other);
};

Далее, std::conj возвращает комплексное число, независимо от типа аргумента. На мой взгляд, это неудачно, потому что в математике нет разницы между скалярным произведением комплексных векторов и некомплексных векторов. Сопряженное действительного числа является действительным числом. Чтобы восстановить это приятное свойство, можно использовать my_conj:

template <typename T> struct is_complex : std::false_type {};
template <typename T> struct is_complex<std::complex<T>> :std::true_type {};


template <typename T>
T my_conj(const T&t) {
    if constexpr (is_complex<T>::value) return std::conj(t);
    else return t;
}

Теперь определение скалярного произведения может быть одинаковым для сложных и не сложных векторов. Полный пример:

#include <vector>
#include <complex>
#include <iostream>
#include <type_traits>

template <typename T> struct is_complex : std::false_type {};
template <typename T> struct is_complex<std::complex<T>> :std::true_type {};


template <typename T>
T my_conj(const T&t) {
    if constexpr (is_complex<T>::value) return std::conj(t);
    else return t;
}


template <typename T>
struct my_vect {
    std::vector<T> data;
    T operator* (const my_vect<T>& other) {
        T result;
        for (size_t i = 0; i < data.size(); i++) result += my_conj(data[i])*other.data[i];
        return result;
    }
};

int main() {
   my_vect<double> x{{1,2,3}};
   std::cout << x*x << '\n';
   my_vect<std::complex<double>> y{{ {1,2} }};
   std::cout << y*y;
}

PS: Вы должны убедиться, что два вектора имеют одинаковый размер.

...