我正在编写一些模板库(假设是线性代数库)并遇到了下一个复杂的错误,我只能在 GCC 上重现该错误(Clang 和 VC++ 按预期工作)。
库由通用模板类型组成,例如
template<class C, int N>
class Vector;
template<class C, int N, int M = N>
class Matrix;
对于一些默认实现,这些没有被使用。 还有一组像这样的接口(interface)类
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 生成错误“错误:对成员‘operator*’的请求不明确”。 但! 如果我更改有错误的行,以便我使用重载函数而不是 'operator*'
v2 = m.mul_impl( v1 ); // <- NO error
或者如果我不是从各自的接口(interface)继承,而是像这样将运算符放在类外:
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*
的情况,这两个函数分别在VecMulInterface
和ScalMulInterface
中,所以它们在不同的作用域中。在其他两种情况下,这两个函数在同一范围内。
您可以使用 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*;
// ...
};
一个更优雅的方法是在接口(interface)中使用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);
}
};
关于c++ - 通过继承重载运算符会导致歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58729481/