Умножение std :: complex <T>на удвоение через шаблонную перегрузку оператора - PullRequest
0 голосов
/ 30 октября 2018

Я хочу мультиплеер std::complex<T> на double, предполагая, что T operator*(const T &t, double d) определено. Поскольку мне нужно сделать это для 3 различных типов T, я попытался написать шаблонную функцию для оператора. Вот пример с T=float.

#include <iostream>
#include <complex>

template <typename T>
std::complex<T> operator*(const std::complex<T> &cd, double d) {
    return std::complex<T>(cd.real() * d, cd.imag());
}

int main() {
    std::complex<float> cf(1.0, 1.0);
    std::complex<double> cd(1.0, 1.0);
    double d = 2.0;

    std::cout << cf * d << std::endl;
    std::cout << cd * d << std::endl;
}

Это дает ошибку компилятора

error: ambiguous overload for ‘operator*’ (operand types are ‘std::complex<double>’ and ‘double’)

Причина понятна, поскольку для T=double моя перегрузка конфликтует с реализацией в <complex>. Первое приведение правой стороны к T (т.е. cf * float(d) в приведенном выше примере) не вариант, так как это привело бы к значительным накладным расходам для некоторых из моих типов данных.

Можно ли как-то сказать компилятору, что он должен игнорировать мою перегрузку для T=double?

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

Можно ли как-то сказать компилятору, что он должен игнорировать мою перегрузку для T = double?

Вы можете использовать SFINAE:

template <typename T>
std::enable_if_t<!std::is_same<double, T>::value, std::complex<T>>
operator*(const std::complex<T> &cd, double d) {
    return std::complex<T>(cd.real() * d, cd.imag());
}

но ваша реализация не соответствует обычной operator*.

Более безопасный способ - ввести собственный тип для переноса double, например:

struct MyWrapper { double d; };

template <typename T>
std::complex<T> operator*(const std::complex<T> &cd, MyWrapper d) {
    return std::complex<T>(cd.real() * d.d, cd.imag());
}
0 голосов
/ 30 октября 2018

std::complex уже определяет operator * в виде

template< class T >
std::complex<T> operator*( const std::complex<T>& lhs, const T& rhs);

Это противоречит вашему собственному operator *, поскольку обе функции разрешают принимать std::complex<double> и double.

Это означает, что вам действительно нужно определить operator * для std::vector<float> и double, чтобы вы могли изменить свою перегрузку на

std::complex<float> operator*(const std::complex<float> &cd, double d) {
    return std::complex<float>(cd.real() * d, cd.imag());
}

А потом

std::cout << cf * d << std::endl;
std::cout << cd * d << std::endl;

будет работать.

Если вы хотите сохранить перегрузку функции шаблона, вы можете использовать SFINAE, чтобы она не компилировалась для случая, когда у вас есть std::complex<double> с помощью

template <typename T, std::enable_if_t<!std::is_same_v<double, T>, bool> = true>
std::complex<T> operator*(const std::complex<T> &cd, double d) {
    return std::complex<T>(cd.real() * d, cd.imag());
}
...