'运营商*'是暧昧的,如何解决歧义?

时间:2016-06-18 11:55:16

标签: c++ c++11 template-meta-programming

考虑我们有一个带有重载operator*的简单数学向量类。 我们对标量乘法和内积也重载operator*。它看起来像这样:

#include <iostream>

template<typename T> struct Vec
{
    template<typename SCALAR_T>
    friend auto operator*(const Vec<T> &v, const SCALAR_T &scalar) 
    {
        return Vec<decltype(T() * SCALAR_T())>{v.x * scalar, v.y * scalar};
    }

    template<typename SCALAR_T>
    friend auto operator*(const SCALAR_T &scalar, const Vec<T> &v)
    {
        return v * scalar;
    }

    template<typename S>
    friend auto operator*(const Vec<T> &v, const Vec<S> &w)
    {
        return v.x * w.x + v.y * w.y;
    }

    T x, y;
};

void templateTest()
{
    Vec<int> vi{ 1, 2 };
    Vec<float> vf{ 2, 3 };
    auto vres = vi * vf;
    std::cout << vres;
}

正如我在这种情况下的预期,VS2015说error C2593: 'operator *' is ambiguous。编译器无法在函数重载之间进行选择。

问题是:如何解决歧义?理想情况下,我希望第3版仅针对两种矢量类型(Vec<A>Vec<B>)进行实例化,对于其他一切我想要使用标量版本。

有没有办法使用类型特征来判断类型是Vec(或不是)的实例化?

我在数学库中看到的常见解决方案是不要为内积重载operator *,而是使用v.dot(w)。这是一个解决方案,但为了理论上的兴趣,我很想知道如何使用模板元编程来做到这一点。

修改

好的,一个解决方案可能是使用SFINAE为运算类型启用前两个重载:

template<typename SCALAR_T, typename = std::enable_if_t<std::is_arithmetic<SCALAR_T>::value>>
friend auto operator*(const Vec<T> &v, const SCALAR_T &scalar) 
{
    return Vec<decltype(T() * SCALAR_T())>{v.x * scalar, v.y * scalar};
}

template<typename SCALAR_T, typename = std::enable_if_t<std::is_arithmetic<SCALAR_T>::value>>
friend auto operator*(const SCALAR_T &scalar, const Vec<T> &v)
{
    return v * scalar;
}

但这不是一个理想的解决方案,它只允许整数和浮点类型为SCALAR_T,但我更希望它接受Vec以外的任何其他内容。所以,问题是,如何实现以下(伪代码):

template<typename SCALAR_T, typename = std::enable_if_t<not_instantiation<SCALAR_T, Vec>::value>>

0 个答案:

没有答案