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

标签 c++ templates inheritance operator-overloading

我正在编写一些模板库(假设是线性代数库)并遇到了下一个复杂的错误,我只能在 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*的情况,这两个函数分别在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*;
    // ...
};

一个更优雅的方法是在接口(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/

相关文章:

javascript - 使用闭包编译器时出现扩展错误

C#:有什么理由不应该声明事件支持字段 protected ?

c++ - GTK:完全摆脱系统主题/CSS

c++ - 使用 Grabcut 在中心初始化等于图像大小 50% 的矩形

C++如何在无法进行类型推导时调用模板化构造函数

c++ - 成员模板,来自 ISO C++ 标准的声明?

c++ - 如何从标识符创建类?

c++ - "std::shared_from_this"不能被它的派生类继承吗?

c++ - 如何转发声明模板类并将其用作 C++ 中的成员数据?

c++模板函数参数推导和函数解析