我可以将enable_if之类的东西与隐式转换运算符一起使用吗
Can I use something like enable_if with an implicit conversion operator?
我有一个(基本完成的(矩阵类(稍后将在本文中介绍(。如果矩阵是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
模板类中,然后让专用化从中继承并添加特殊功能。另一种选择是无论如何实现该方法,并在其中放置一个静态断言,以在实例化该方法时导致编译器错误。请注意,这将阻止类的显式实例化。
相关文章:
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 构造函数和转换运算符之间的重载解析
- 分配给转换运算符失败-C++
- 转换运算符不适用于sleep_until
- 继承模板化转换运算符
- 模板转换运算符在 clang 6 和 clang 7 之间的区别
- 如何在模板化转换运算符中消除此构造的歧义?
- 为什么选择转换运算符的重载?
- 如何避免强制转换运算符 () 和访问运算符 [] 冲突?
- 如果可能的话,C++总是更喜欢右值引用转换运算符而不是常量左值引用吗?
- 了解转换运算符的选择C++
- 多个隐式转换运算符
- 这个typedef和转换运算符语法是什么意思
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 类模板忽略了用户定义的转换运算符(非模板不忽略)
- 为什么在std::for_each()返回时调用转换运算符
- 为什么 std::optional 的强制转换运算符被忽略了
- 使用用户定义的转换运算符推导函数模板参数
- 模板转换运算符的分辨率不明确
- 统一初始化是隐式发生的,即使 int 强制转换运算符是使用 explicit 关键字声明的.原因是什么?