通过继承重载运算符会导致歧义

Operator overloading through inheritance causes ambiguity

本文关键字:歧义 运算符 继承 重载      更新时间:2023-10-16

我正在编写一些模板库(比如说线性代数库(,并遇到了下一个复杂的错误,我只能在GCC上重现(Clang和VC++按预期工作(。

该库由像这样的通用模板类型组成

template<class C, int N>
class Vector;
template<class C, int N, int M = N>
class Matrix;

在一些默认实现中,这些实现没有被使用。还有一组类似的接口类

template<class C, int N>
struct VecMulInterface
{
Vector<C, N> operator*(const Vector<C, N>& v)
{
return static_cast<Matrix<C, N, N>*>(this)->mul_impl(v);
}
};
template<class C, int N>
struct ScalMulInterface
{
Matrix<C, N, N> operator*(const C& c)
{
return static_cast<Matrix<C, N, N>*>(this)->mul_impl(c);
}
};

以及各自的实现提供商

template<class C, int N>
struct MulImpl
{
public:
Vector<C, N> mul_impl(const Vector<C, N>& v) const
{        
return {}; // imagine some logic here
}
Matrix<C, N, N> mul_impl(const C& c) const
{     
return {}; // imagine some logic here
}
};

最后,我有模板专业化,它们使用上面的一切:

template<class C>
class Vector<C, 2>
{
public:
C items[2];
// ...
};
template<class C>
class Matrix<C, 2, 2>:
public VecMulInterface<C, 2>,
public ScalMulInterface<C, 2>,
public MulImpl<C, 2>
{
public:
C items[4];
// ...
};

我试着这样使用它们:

Matrix<int, 2, 2> m;
Vector<int, 2> v1, v2;
v2 = m * v1; // <- error

现在GCC产生一个错误"error:对成员'operator*'的请求不明确"。但是如果我在错误的情况下更改线路,从而使用过载功能而不是"操作员*">

v2 = m.mul_impl( v1 ); // <- NO error

或者,如果不是从各自的接口继承,而是像这样将运算符放在类外:

template<class C, int N>
Vector<C, N> operator*(const Matrix<C, N, N>& m, const Vector<C, N>& v)
{
return m.mul_impl(v);
}
template<class C, int N>
Matrix<C, N, N> operator*(const Matrix<C, N, N>& m, const C& c)
{
return m.mul_impl(c);
}

一切都很好。(VC++和Clang似乎在所有情况下都能正常工作(

有人能解释这种行为的原因吗?这是编译器错误还是我在代码中的某个地方出现了"未定义行为"?

不同作用域中的函数不会重载。对于成员operator*的情况,这两个函数分别位于VecMulInterfaceScalMulInterface中,因此它们处于不同的范围中。在另外两种情况下,这两种功能在同一范围内。

您可以使用using声明将它们提升到相同的范围:

template<class C>
class Matrix<C, 2, 2> :
public VecMulInterface<C, 2>,
public ScalMulInterface<C, 2>,
public MulImpl<C, 2>
{
public:
using VecMulInterface<C, 2>::operator*;
using ScalMulInterface<C, 2>::operator*;
// ...
};

一种更优雅的方法是在接口中使用friend函数:

template <class C, int N>
struct VecMulInterface
{
friend Vector<C, N> operator*(const Matrix<C, N, N>& m, const Vector<C, N>& v)
{
return m.mul_impl(m, v);
}
};