我在自己的库中编写了一个通用的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*
,但是落入第二个,因为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
重载所有相关的Matrix
运算符。operator*
中重新实现Matrix4x4
。会有重复的代码,或者,如果我可以设法使用强制转换来调用Matrix::operator*
的正确重载,那么,这仍然会很麻烦。Matrix4x4::operator Matrix<float,4,4>()
。它似乎不起作用,但我也可能在那里做错了。无论如何,我知道这会创建一个不受欢迎的对象副本。我现在所处的位置。还有其他想法吗?也许我首先做错了什么? 我确定我会从中学到一些东西,所以非常欢迎任何帮助(:
修改
要求Matrix
和Matrix4x4
的定义:
template<typename T, int Y, int X>
class Matrix
{
private:
T data[Y][X];
...
};
class Matrix4x4 : public Matrix<float,4,4>
{
...
};
答案 0 :(得分:1)
按如下方式使用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
}
};
成员矩阵乘法比非成员更好地处理歧义。友元运算符就是我所说的Koenig运算符,必须在类中内联实现。你可以调用另一个函数并实现那个函数。
你也可以乱搞sfinae或标签调度,但上面的内容简洁明了。请注意,标量只允许在lhs上,因为Matrix * Scalar
是......古怪的。 Scalar * Matrix
更为传统。
答案 1 :(得分:0)
正如Yakk所提出的,使用df2[j,4]
作为返回类型允许删除哪些类型不能相乘的重载,从而迫使编译器使用正确的版本。据我了解,这是SFINAE的使用:
decltype(Y*U)
但是后来一些运算符没有可以用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;
}
这些术语声明的返回类型。最好它必须返回与左手类型operator+=
相同的类型。对于这些情况,*this
可以按如下方式使用:
enable_if