【问题标题】:Multiplying a std::complex<T> by double via templated operator overload通过模板化运算符重载将 std::complex<T> 乘以双倍
【发布时间】:2019-04-03 17:50:39
【问题描述】:

假设T operator*(const T &amp;t, double d) 已定义,我想将std::complex&lt;T&gt;double 进行多重播放。由于我需要为 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,我的重载与&lt;complex&gt; 中的实现发生冲突。首先将右侧转换为T(即上例中的cf * float(d))不是一种选择,因为这会给我的一些数据类型带来很大的开销。

有什么方法可以告诉编译器它应该忽略我对T=double 的重载?

【问题讨论】:

    标签: c++ templates operator-overloading std operator-keyword


    【解决方案1】:

    有什么方法可以告诉编译器它应该忽略我的 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 的风险)。
    【解决方案2】:

    std::complex 已经定义了一个operator *,格式为

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

    这与您自己的 operator * 冲突,因为这两个函数都解析为采用 std::complex&lt;double&gt;double

    这意味着您实际上只需要为std::vector&lt;float&gt;double 定义一个operator *,这样您就可以将您的重载更改为

    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&lt;double&gt; 的情况下通过使用

    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());
    }
    

    【讨论】:

    • 是的,关键是要保留模板函数,因为我不会为 3 个不同的 T`s 重写此函数
    • @T-Rex96 请注意,这将导致不同的结果。 (a + bi) * c == (ac + bci),您的运营商只提供(ac + bi)。您可以在这个实时示例中看到差异:coliru.stacked-crooked.com/a/b59c7f2b181c87b5
    • 是的,这只是一个错字,在我的实际代码中,操作符的行为与预期一致
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多