我可以使用带有隐式转换运算符的enable_if之类的东西吗?

时间:2011-10-07 23:17:39

标签: c++ templates c++11 enable-if

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

4 个答案:

答案 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模板类,然后从中继承特殊化并添加特殊功能。另一种方法是无论如何都要实现该方法,并在其中放置一个静态断言,以便在实例化时导致编译器错误。请注意,这会阻止您的类的显式实例化。