我可以将enable_if之类的东西与隐式转换运算符一起使用吗

Can I use something like enable_if with an implicit conversion operator?

本文关键字:转换 运算符 一起 enable if 我可以      更新时间:2023-10-16

我有一个(基本完成的(矩阵类(稍后将在本文中介绍(。如果矩阵是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;
}

替代方案:

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;
};

这里有一个粗略的破解方法,使用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];
  }
};

通过一些工作,可能也会使编译器错误更有意义。

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开始生效。

不,不能将enable_if与隐式转换运算符一起使用,没有可以应用它的类型。将所有常用功能移动到matrix_base模板类中,然后让专用化从中继承并添加特殊功能。另一种选择是无论如何实现该方法,并在其中放置一个静态断言,以在实例化该方法时导致编译器错误。请注意,这将阻止类的显式实例化。