Я хочу воспроизвести 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
?
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());
}
@ T-Rex96 Обратите внимание, что это приведет к другим результатам. (a + bi) * c == (ac + bci)
, где ваш оператор дает только (ac + bi)
. Вы можете увидеть разницу в этом живом примере: coliru.stacked-crooked.com/a/b59c7f2b181c87b5
Да, это была всего лишь опечатка, в моем собственном коде оператор ведет себя так, как ожидалось
Is there any way I can tell the compiler that it should ignore my overload for 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, i) * 5.
дает (0, i)
вместо (0, 5i)
.
О, это была всего лишь опечатка: D Так в таком случае реализация SFINAE подойдет?
Проблема, которую я вижу, заключается в том, что вы создаете библиотеку (поскольку другие могут также делать «то же самое», и вы рискуете нарушить ODR).
Да, дело в том, чтобы сохранить функцию шаблона, так как я не пишу, чтобы переписывать эту функцию для 3 разных T