C++ 乘法子类型时编译器选择了错误的运算符*

C++ 乘法子类型时编译器选择了错误的运算符*,c++,inheritance,operator-overloading,C++,Inheritance,Operator Overloading,我在自己的一个库中编写了一个通用矩阵类,其中包含了像+、-、*这样的好操作符。特别是,它有(功能体并不重要,所以您可以忽略它们,但我稍后仍会提到它,所以最好有): 这里的mu-camera.mu-projectionmatrix也是一个Matrix4x4矩阵 这应该调用第一个操作符*,但属于第二个操作符,因为gcc在第二个重载中给出了一个错误,如下所示: result[j][i] += left[j][i] * right; 错误消息: Matrix.hpp|169|e

我在自己的一个库中编写了一个通用矩阵类,其中包含了像+、-、*这样的好操作符。特别是,它有(功能体并不重要,所以您可以忽略它们,但我稍后仍会提到它,所以最好有):

这里的
mu-camera.mu-projectionmatrix
也是一个
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:|
...
我的猜测是,
Matrix4x4
并不完全是
Matrix
,而只是一个子类型,一些规则使gcc选择不涉及类型转换的最佳重载

我不知道如何解决这个问题。我考虑过几种解决方案,但没有一种是好的:

  • 删除将接收内置类型的运算符,从而强制编译器选择唯一剩余的重载。这是可行的,但迫使我从一个看似完美工作的库中删除一个特性
  • 使用组合而不是继承,并根据
    矩阵
    重载所有相关的
    Matrix4x4
    运算符
  • Matrix4x4
    中重新实现
    运算符*
    。可能会有重复的代码,或者,如果我能够通过强制转换调用正确的
    Matrix::operator*
    ,那么这仍然是一个麻烦
  • 创建
    Matrix4x4::运算符矩阵()
    。这似乎不起作用,但我可能在那里也做错了什么。无论如何,我知道这会创建一个不受欢迎的对象副本
那就是我现在所在的地方。还有别的想法吗?也许一开始我做错了什么? 我相信我会从中学到一些东西,所以任何帮助都是非常受欢迎的(:

编辑:

Matrix
Matrix4x4
的定义如下:

template<typename T, int Y, int X>
class Matrix
{
private:
    T data[Y][X];
    ...
};

class Matrix4x4 : public Matrix<float,4,4>
{
    ...
};
模板
类矩阵
{
私人:
T数据[Y][X];
...
};
Matrix4x4类:公共矩阵
{
...
};

使用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
  }
};
template<typename T, typename U, typename = decltype(std::declval<T const&>()+std::declval<U const&>())>
struct can_add
{
    static const bool value = true;
};
template<typename T, typename U>
struct can_add<T,U>
{
    static const bool value = false;
};

template<typename T, int Y, int X>
class Matrix
{
private:
    T data[Y][X];

public:
    // ...
    template<typename U, typename = std::enable_if<freon::can_add<T,U>::value>>
    void operator+=(const Matrix<U, Y, X> & other)
    {
        for (int j = 0; j < Y; ++j)
            for (int i = 0; i < X; ++i)
                data[j][i] += other[j][i];
    }
    template<typename U, typename = std::enable_if<freon::can_add<T,U>::value>>
    void operator+=(const U & other)
    {
        for (int j = 0; j < Y; ++j)
            for (int i = 0; i < X; ++i)
                data[j][i] += other;
    }
}
模板
类矩阵{
// ...
公众:
//计算'T*U'的返回值:
模板
使用R=decltype(std::declval()*std::declval());
//如果需要的话,可能会在上面加上一个“decation\u t”(但谁会回来呢
//来自二进制文件“*”)的引用
//RHS上的矩阵乘法
//该矩阵的RHS维数和LHS维数
//RHS矩阵的,必须匹配。接受带有
//不同的基础T。
模板
矩阵算子*(Matrixconst&rhs)常数;
//你可以在这里实现这个操作符,也可以在下面实现
//在相同的头文件中。
//这是Koenig运算符。它是一个
//是*不是*模板,其中左侧是
//T型,右边是我们自己的类型。
友元矩阵算子*(
T常数和lhs,矩阵常数和rhs
){
//在这里实施
}
};
“成员矩阵乘法”比“非成员矩阵乘法”更能处理歧义。friend运算符就是我所说的Koenig运算符,必须在类中内联实现。您可以调用另一个函数,并在外联实现该函数


您也可以随意处理sfinae或标记分派,但上面的操作是干净而简单的。请注意,标量只允许在lhs上使用,因为
Matrix*Scalar
非常奇怪。
Scalar*Matrix
更为传统。

正如雅克建议的那样,使用
decltype(Y*U)
as return type允许删除类型不能相乘的重载,从而迫使编译器使用正确的版本。据我所知,这是SFINAE的一种用法:

template<typename T, int X, int Y, typename U, int Z>
Matrix<decltype(std::declval<T const&>()*std::declval<U const&>()),Y,X>
operator*(const Matrix<T,Y,X> & left, const Matrix<U,X,Z> & up)
{
    Matrix<T,Y,Z> result = Matrix<T, Y, Z>::Zero;
    for (unsigned int j = 0; j<Y; j++)
        for (unsigned int i = 0; i<X; i++)
            for (unsigned int k = 0; k<Z; k++)
                result[j][k] += left[j][i] * up[i][k];
    return result;
}
template<typename T, int Y, int X, typename U>
Matrix<decltype(std::declval<T const&>()*std::declval<U const&>()),Y,X>
operator*(const Matrix<T,Y,X> & left, const U & right)
{
    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;
}

我们应该猜测模板参数是什么,或者
Matrix
matrix4x4x4
的定义是什么样的吗?我们不认为这是相关的。友好地询问会有所帮助。AxB与BxC相乘,而不是AxB。第二个函数中的
typename U
被推导为
Matrix4x4 const&
,这是一个be如果不匹配矩阵常数&,则必须限制可与SFINAE的第二个函数一起使用的类型。还要注意的是,函数返回的是
矩阵
,而不是派生类,这可能不是您想要的。是的,@Yakk。我选择简化代码以提高可读性。有趣的是,
decltype
act按逻辑删除错误的
运算符*
模板矩阵运算符*(常数矩阵和左、常数矩阵和上)
。但是
操作符+=
也会遇到同样的问题,无法将decltype放入返回类型中,因为它必须返回
this
的类型。此外,我不确定Koenig操作符有何帮助。我不需要它用于操作符*,也不需要它用于+=。或者是吗?@gab declryoue ssfinae对于r上面的代码可以工作。它应该与“+=”一起工作。
template<typename T, int X, int Y, typename U, int Z>
Matrix<decltype(std::declval<T const&>()*std::declval<U const&>()),Y,X>
operator*(const Matrix<T,Y,X> & left, const Matrix<U,X,Z> & up)
{
    Matrix<T,Y,Z> result = Matrix<T, Y, Z>::Zero;
    for (unsigned int j = 0; j<Y; j++)
        for (unsigned int i = 0; i<X; i++)
            for (unsigned int k = 0; k<Z; k++)
                result[j][k] += left[j][i] * up[i][k];
    return result;
}
template<typename T, int Y, int X, typename U>
Matrix<decltype(std::declval<T const&>()*std::declval<U const&>()),Y,X>
operator*(const Matrix<T,Y,X> & left, const U & right)
{
    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;
}
template<typename T, typename U, typename = decltype(std::declval<T const&>()+std::declval<U const&>())>
struct can_add
{
    static const bool value = true;
};
template<typename T, typename U>
struct can_add<T,U>
{
    static const bool value = false;
};

template<typename T, int Y, int X>
class Matrix
{
private:
    T data[Y][X];

public:
    // ...
    template<typename U, typename = std::enable_if<freon::can_add<T,U>::value>>
    void operator+=(const Matrix<U, Y, X> & other)
    {
        for (int j = 0; j < Y; ++j)
            for (int i = 0; i < X; ++i)
                data[j][i] += other[j][i];
    }
    template<typename U, typename = std::enable_if<freon::can_add<T,U>::value>>
    void operator+=(const U & other)
    {
        for (int j = 0; j < Y; ++j)
            for (int i = 0; i < X; ++i)
                data[j][i] += other;
    }
}