【问题标题】:Operator overloading through inheritance causes ambiguity通过继承重载运算符会导致歧义
【发布时间】:2019-11-06 11:48:35
【问题描述】:

我正在编写一些模板库(假设是线性代数库)并遇到了下一个复杂的错误,我只能在 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 产生一个错误“错误:对成员 ‘operator*’ 的请求不明确”。 但! 如果我更改错误行,以便我使用重载函数而不是 '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 似乎在所有情况下都能正常工作)

谁能解释这种行为的原因?这是编译器错误还是我在代码中的某处踩到了“未定义的行为”?

【问题讨论】:

    标签: c++ templates inheritance operator-overloading


    【解决方案1】:

    不同作用域的函数不会过载。对于成员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);
        }
    };
    

    【讨论】:

      猜你喜欢
      • 2013-10-17
      • 2011-12-05
      • 2021-05-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-05
      • 2020-12-16
      • 1970-01-01
      • 2012-04-05
      相关资源
      最近更新 更多