部分专业化和SFINAE
Partial specialization and SFINAE
假设我有以下矩阵模板类,并且需要将向量表示为1 x RowSize或ColSize x 1矩阵(这样我就可以重用许多与向量兼容的矩阵运算符:将2个矩阵相乘,将矩阵乘以标量等):
template <class T, size_t ColumnSize, size_t RowSize>
struct Matrix {
T [ColumnSize][RowSize];
}
我有两个问题:
1) 如果我没有错的话,我可以通过部分专业化或在矩阵方法上使用SFINAE来实现这一点(例如,当ColSize或RowSize为1时启用"length"方法)。上述方案的优缺点是什么?
2) 如果我选择部分专业化,有没有一种方法可以为行和列向量定义一个专业化,而不是这样:
template <class T, size_t ColumnSize>
struct Matrix<T, ColumnSize, 1> {
T length() const;
T [ColumnSize][RowSize];
}
template <class T, size_t RowSize>
struct Matrix<T, 1, RowSize> {
T length() const;
T [ColumnSize][RowSize];
}
这实际上取决于要求是"通用矩阵不得具有长度方法"(然后应使用SFINAE或继承),还是"不能在通用矩阵上调用length
"(然后length
主体内部的static_assert适用)。第三种选择是什么都不做,并使length
适用于一般矩阵,但仍有其他操作仅适用于向量。
对于"一般矩阵不能有长度方法"。为了节省空间,我将使用int
和较短的符号名称。应该使用std::integral_constant
而不是int_
。由于语言限制,如果参数是非类型参数,则禁止专门处理更复杂的计算,因此需要int_
包装器。因此,我们ḿ将paramer设为类型,并将值包装到其中。下面不使用SFINAE,而是使用继承。使用向量混合基类的d()
,您可以随时从混合类中访问向量的数据。
template<int> struct int_;
template<typename D, typename S>
struct V { };
template<typename T, int A, int B>
struct M : V<M<T, A, B>, int_<A * B>> {
T data[A][B];
};
template<typename T, int A, int B>
struct V<M<T, A, B>, int_<A + B - 1>> {
int length() const { return A * B; }
M<T, A, B> *d() { return static_cast<M<T, A, B>*>(this); }
const M<T, A, B> *d() const { return static_cast<const M<T, A, B>*>(this); }
};
现在是
int main() {
M<float, 1, 3> m1; m1.length();
M<float, 3, 1> m2; m2.length();
// M<float, 3, 2> m3; m3.length(); error
}
对于"不能在通用矩阵上调用length
",可以使用"static_assert">
template<typename T, int A, int B>
struct M {
int length() const {
static_assert(A == 1 || B == 1, "must not be called on a matrix!");
return A * B;
}
T data[A][B];
};
选择最合适的
SFINAE只能根据自己的参数禁用模板声明。使用封闭类的参数禁用非模板成员函数(如length
)有点不自然。技术看起来像这样:
template <class T, size_t RowSize, size_t ColumnSize>
struct Matrix {
// SFINAE turns a non-template into a template.
// Introduce a fake dependency so enable_if resolves upon function call.
template< typename size_t_ = size_t >
static constexpr
// Now write the actual condition within the return type.
std::enable_if_t< RowSize == 1 || ColumnSize == 1
, size_t_ > length() const;
{ return RowSize * ColumnSize; }
T [ColumnSize][RowSize];
}
如果你能忍受这种丑陋,那么你就会得到你想要的:一种所需类型的函数,当条件不满足时,它就会完全消失。不需要其他支持。
另一方面,部分专门化会影响整个类的定义。由于在每个部分专门化中复制整个类通常是一种糟糕的设计,因此正如Johannes所描述的那样,使用继承。
为了给他的答案添加一个替代方案,SFINAE可以在偏专业化中使用,以避免巧妙的代数和int_
问题。
// Add "typename = void" for idiomatic class SFINAE.
template<size_t RowSize, size_t ColumnSize, typename = void>
struct maybe_vector_interface { }; // Trivial specialization for non-vectors
// Partial specialization for vectors:
template<size_t RowSize, size_t ColumnSize>
struct maybe_vector_interface< RowSize, ColumnSize,
std::enable_if_t< RowSize == 1 || ColumnSize == 1 > > {
static constexpr int length() const
{ return RowSize * ColumnSize; }
};
template<typename T, size_t RowSize, size_t ColumnSize>
struct Matrix
: maybe_vector_interface<RowSize, ColumnSize> {
T data[RowSize][ColumnSize];
};
- 如何使用默认参数等选择模板专业化
- 模板化建造师专业化
- 为什么使用SFINAE而不是函数重载
- 类模板的成员功能的定义在单独的TU中完全专业化
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 提供与TMP和SFINAE的通用接口
- 部分专业化和嵌套模板
- 模板专业化可以进入我的.cpp吗?
- "Inverse SFINAE"避免模棱两可的过载
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- 是否可以混合使用SFINAE和模板专业化?
- 使用void_t的多个SFINAE类模板专业化
- 部分模板专业化 SFINAE
- 部分专业化和SFINAE
- variadic模板专业化,std :: enable_if,sfinae
- 使用 SFINAE、约束或概念限制专业化
- 禁用默认模板,仅通过sfinae使用专业化