我有一个(基本完成的)矩阵类(本文稍后)。如果矩阵是1x1矩阵,那么我希望隐式转换为支持类型(例如,1x1浮点矩阵应该转换为浮点数)。
有没有办法在不创建专业化并复制Matrix内的所有方法的情况下做到这一点? (例如使用std::enable_if
之类的东西?)当且仅当ROWS == COLS == 1时,我基本上想要启用隐式转换。
template <std::size_t ROWS, std::size_t COLS = 1, typename BackingType = float>
class Matrix
{
BackingType data[ROWS][COLS];
public:
Matrix()
{
for(std::size_t rdx = 0; rdx < ROWS; ++rdx)
{
for (std::size_t cdx = 0; cdx < COLS; ++cdx)
{
data[rdx][cdx] = 0;
}
}
}
const BackingType& Member(std::size_t index) const
{
assert(index < ROWS*COLS);
return *(static_cast<BackingType*>(&data[0][0]) + index);
}
BackingType& Member(std::size_t index)
{
assert(index < ROWS*COLS);
return *(static_cast<BackingType*>(&data[0][0]) + index);
}
const BackingType& Member(std::size_t rowIndex, std::size_t colIndex) const
{
assert(rowIndex < ROWS);
assert(colIndex < COLS);
return data[rowIndex][colIndex];
}
BackingType& Member(std::size_t rowIndex, std::size_t colIndex)
{
assert(rowIndex < ROWS);
assert(colIndex < COLS);
return data[rowIndex][colIndex];
}
Matrix<COLS, ROWS, BackingType> Transpose() const
{
Matrix<COLS, ROWS, BackingType> result;
for(std::size_t rowIdx = 0; rowIdx < ROWS; rowIdx++)
{
for (std::size_t colIdx = 0; colIdx < COLS; ++colIdx)
{
result.Member(colIdx, rowIdx) = Member(rowIdx, colIdx);
}
}
return result;
}
template <std::size_t otherRows, std::size_t otherCols>
Matrix<ROWS + otherRows, COLS, BackingType> AugmentBelow(const Matrix<otherRows, otherCols, BackingType>& other)
{
static_assert(COLS == otherCols, "Columns must match for a vertical augmentation.");
Matrix<ROWS + otherRows, COLS, BackingType> result;
for (std::size_t curRow = 0; curRow < ROWS; ++curRow)
{
for (std::size_t curCol = 0; curCol < COLS; ++curCol)
{
result.Member(curRow, curCol) = Member(curRow, curCol);
}
}
for (std::size_t curRow = ROWS; curRow < (ROWS + otherRows); ++curRow)
{
for (std::size_t curCol = 0; curCol < COLS; ++curCol)
{
result.Member(curRow, curCol) = other.Member(curRow - ROWS, curCol);
}
}
return result;
}
template <std::size_t otherRows, std::size_t otherCols>
Matrix<ROWS, COLS + otherCols, BackingType> AugmentRight(const Matrix<otherRows, otherCols, BackingType>& other)
{
static_assert(ROWS == otherRows, "Rows must match for a vertical augmentation.");
Matrix<ROWS, COLS + otherCols, BackingType> result;
for (std::size_t curRow = 0; curRow < ROWS; ++curRow)
{
for (std::size_t curCol = 0; curCol < COLS; ++curCol)
{
result.Member(curRow, curCol) = Member(curRow, curCol);
}
for (std::size_t curCol = COLS; curCol < (COLS + otherCols); ++curCol)
{
result.Member(curRow, curCol) = other.Member(curRow, curCol - COLS);
}
}
return result;
}
static Matrix<ROWS, COLS, BackingType> Identity()
{
static_assert(ROWS == COLS, "Identity matrices are always square.");
Matrix<ROWS, COLS, BackingType> result;
for (std::size_t diagonal = 0; diagonal < ROWS; ++diagonal)
{
result.Member(diagonal, diagonal) = 1;
}
return result;
}
};
template <std::size_t leftRows, std::size_t leftCols, std::size_t rightRows, std::size_t rightCols, typename BackingType>
inline Matrix<leftRows, rightCols, BackingType> operator*(const Matrix<leftRows, leftCols, BackingType>& left, const Matrix<rightRows, rightCols, BackingType>& right)
{
static_assert(leftCols == rightRows, "Matrix multiplications require that the left column count and the right row count match.");
Matrix<leftRows, rightCols, BackingType> result;
for (std::size_t i = 0; i < leftRows; ++i)
{
for (std::size_t j = 0; j < rightCols; ++j)
{
BackingType curItem = 0;
for (std::size_t k = 0; k < leftCols; ++k)
{
curItem += left.Member(i, k) * right.Member(k, j);
}
result.Member(i, j) = curItem;
}
}
return result;
}
template <std::size_t rows, std::size_t cols, typename BackingType>
inline Matrix<rows, cols, BackingType> operator*(BackingType val, const Matrix<rows, cols, BackingType>& target)
{
Matrix<rows, cols, BackingType> result = target;
for (std::size_t i = 0; i < rows; ++i)
{
for (std::size_t j = 0; j < cols; ++j)
{
result *= val;
}
}
return result;
}
答案 0 :(得分:6)
替代方案:
template<typename T, int Rows, int Cols>
struct matrix {
template<
// we need to 'duplicate' the template parameters
// because SFINAE will only work for deduced parameters
// and defaulted parameters count as deduced
int R = Rows
, int C = Cols
// C++11 allows the use of SFINAE right here!
, typename = typename std::enable_if<
(R == 1 && C == 1)
>::type
>
operator T() const;
};
答案 1 :(得分:5)
使用conditional
而不是enable_if
,这是一个粗略的黑客攻击:
#include <functional>
template <typename T, int N, int M>
struct Matrix
{
struct IncompleteType;
T buf[N * M];
operator typename std::conditional<N == 1 && M == 1, T, IncompleteType<T>>::type () const
{
return buf[0];
}
};
通过一些工作,可能会使编译器错误更有意义。
答案 2 :(得分:2)
template <typename T, int N, int M>
struct Matrix
{
T buf[N * M];
operator typename std::conditional<N == 1 && M == 1, T, void>::type () const
{
return buf[0];
}
};
在这种情况下,无需定义IncompleteType
。使用void
就足够了,因为具有void
类型的函数不应返回任何值,但它会返回一些内容。这导致替换失败并且SFINAE开始了。
答案 3 :(得分:0)
不,您不能将enable_if
与隐式转换运算符一起使用,没有可以应用它的类型。将所有常用功能移至matrix_base
模板类,然后从中继承特殊化并添加特殊功能。另一种方法是无论如何都要实现该方法,并在其中放置一个静态断言,以便在实例化时导致编译器错误。请注意,这会阻止您的类的显式实例化。