考虑我们有一个带有重载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>>