我在自己的库中编写了一个通用的Matrix类,其中包含+,-,*等漂亮的运算符。特别是,它具有(功能主体并不重要,因此您可以忽略它们,但以后我仍会对其进行引用,因此很高兴拥有):

template<typename T, int X, int Y>
Matrix<T,Y,X> operator*(const Matrix<T,Y,X> & left, const Matrix<T,Y,X> & up)
{
    Matrix<T,Y,X> result = Matrix<T,Y,X>::Zero;
    for (unsigned int j = 0; j<Y; j++)
        for (unsigned int i = 0; i<X; i++)
            for (unsigned int k = 0; k<X; k++)
                result[j][k] += left[j][i] * up[i][k];
    return result;
}
template<typename T, int Y, int X, typename U>
Matrix<T,Y,X> operator*(const Matrix<T,Y,X> & left, const U & right)
{
    // Expected to handle build-in types
    Matrix<T, Y, X> result = Matrix<T, Y, X>::Zero;
    for (int j = 0; j < Y; ++j)
        for (int i = 0; i < X; ++i)
            result[j][i] += left[j][i] * right;
    return result;
}

然后,我写了Matrix4x4,它是Matrix的子类型,专门用于3D转换(例如旋转和平移),因此它具有成员函数。当然,Matrix4x4是一个坏名字,我保证会解决的。

在使用Matrix4x4的代码中的某些时候,我使用operator*:
// std::vector<Matrix4x4> mstackvertices({Matrix4x4::Identity});
mstackvertices.push_back(mstackvertices.back() * m_camera.m_projectionmatrix);

这里m_camera.m_projectionmatrix也是Matrix4x4

这应该调用第一个operator*,但会进入第二个Matrix4x4,因为gcc在第二行中在第二个重载内给我一个错误,在下一行:
            result[j][i] += left[j][i] * right;

错误信息:
Matrix.hpp|169|error: no match for ‘operator*’ (operand types are ‘const float’ and ‘const swegl::Matrix4x4’)|
Matrix.hpp|169|note: candidates are:|
...

我的猜测是Matrix并非完全是Matrix4x4,而仅仅是一个子类型,某些规则适用,使gcc选择不涉及类型转换的最佳重载。

我不确定如何解决此问题。我已经考虑了几种解决方案,但似乎都不是一件好事:
  • 删除将接收内置类型的运算符,从而迫使编译器选择仅剩下的重载。这可行,但是迫使我从一个看似完美的库中删除了一个功能。
  • 使用合成代替继承,并根据Matrix重载所有相关operator*的运算符。
  • Matrix4x4中重新实现Matrix::operator*。将会有重复的代码,或者,如果我可以设法通过强制类型转换正确调用Matrix4x4::operator Matrix<float,4,4>()重载,那仍然很麻烦。
  • 创建Matrix。它似乎没有用,但是那儿我可能也做错了。无论如何,我知道这会创建该对象的不受欢迎的副本。

  • 那就是我现在的位置。还有其他想法吗?也许我一开始做错了什么?
    我敢肯定,我将从中学到一些东西,因此,我们非常欢迎任何帮助(:

    编辑:

    要求的Matrix4x4和ojit_code的定义:
    template<typename T, int Y, int X>
    class Matrix
    {
    private:
        T data[Y][X];
        ...
    };
    
    class Matrix4x4 : public Matrix<float,4,4>
    {
        ...
    };
    

    最佳答案

    如下使用Koenig运算符:

    template<class T, int X, int Y>
    class Matrix{
      // ...
    public:
      // calculates the return value of `T*U`:
      template<class U>
      using R=decltype(std::declval<T const&>()*std::declval<U const&>());
      // maybe addin a `decay_t` to the above, if required.  (But who returns
      // a reference from binary `*`?)
    
      // Multiplication by a matrix on the RHS
      // The RHS dimension of this matrix, and the LHS dimension
      // of the RHS matrix, must match.  Accepts matrices with a
      // different underlying T.
      template<class U, int Z>
      Matrix<R<U>,X,Z> operator*(Matrix<U,Y,Z>const& rhs)const;
      // you can implement this operator here, or you can do it below
      // in the same header file.
    
      // This is the Koenig operator.  It is a friend operator that
      // is *not* a template, where the left hand side is a scalar of
      // type T, and the right hand side is our own type.
      friend Matrix<R<T>,X,Y> operator*(
        T const& lhs, Matrix const& rhs
      ){
        // implement here
      }
    };
    

    成员矩阵乘法处理歧义性要比非成员矩阵好。 friend 运算符就是我所谓的Koenig运算符,必​​须在类中内联实现。您可以调用另一个函数并实现它。

    您也可以搞混sfinae或标签分发,但是上面的内容很干净而且很简单。请注意,标量只允许在lhs上使用,因为Matrix * Scalar是...古怪的。 Scalar * Matrix是更常规的。

    关于c++ - 乘子类型时,编译器选择了错误的运算符*,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33337379/

    10-11 06:03