【发布时间】:2019-03-22 16:08:40
【问题描述】:
简短的问题是:如何使模板化函数表现不同,基于 关于参数概念支持。
我的意思是,如果类型 T 实现了某个概念,那么我的函数应该对它进行特殊处理,而不是其他类型。
问题扩展是:应该如何声明这个函数,以便最终用户可以在其声明中看到它支持哪些类型(用户,不应该为了获得这些知识而检查定义)。
这是我的具体示例,我使用 B. Stroustrup 在“C++ 编程语言”中提出的“概念”思想,即 constexpr 谓词,而不是 c++20 的概念,因为我的编译器没有支持它,但是:
template<typename T>
bool constexpr is_matrix() { ... }
template<size_t N, size_t M, typename T>
class Matrix {
...
template<typename U>
Matrix& operator+=(const U& v) {
if constexpr (is_matrix<U>()) {
// handle matrix case
} else {
// handle scalar case
}
}
...
}
这个例子取自我用来研究软件渲染的简单 Matrix/Vector 库。我的想法是要求类型 U 来满足我的概念(支持所有必要的操作,提供必要的类型别名),而不是要求它是我的 Matrix 类型,当检查失败时应该处理它作为标量。
那么这里可以应用哪些技术来使最终用户的代码更清晰,还有比 constexpr if 更好的方法来提供基于概念的参数多态性吗?
我能想出的解决这个问题的唯一方法是使用 enable_if,如下所示:
...
template<typename U, typename =
enable_if_t<is_convertible_v<U, T> ||
(is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>>
Matrix& operator+=(const U& v) {
...
这非常冗长,不能称为漂亮,顺便说一句,我更喜欢静态断言类型应该是可转换的或者是可转换值的矩阵,而不是隐藏这个运算符。
编辑:重新考虑我的解决方案,我实际上可以在定义中使用 static_assert,但仍然在声明中提供断言标准
template<typename U, bool check = is_convertible_v<U, T> || (is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>
... {
static_assert(check, "Type U should be either convertible to T, or being a matrix of convertible values");
}
EDIT2:可以进一步改进为更具可读性的变体:
...
template <typename U, bool check = std::disjunction_v<
compatible_type<This, U>,
compatible_matrix<This, U>,
compatible_vector<This, U>>>
Matrix& operator+=(const U& v) {
assert_compatible<check>();
...
【问题讨论】:
-
您打算以任意尺寸进行渲染吗?软件渲染通常只需要 3x3、3x4 或 4x4 矩阵。如果您尝试使用模板和概念将其抽象为遗忘,您最终可能会得到更臃肿、更难理解的代码。
-
不,我渲染 3D,所以主要是 4x4 矩阵,而且我的向量实际上是 3x1 和 4x1 矩阵的别名。我以前在 C++03 是最近的标准时就知道 C++,所以我同时完成了两个任务:学习软件渲染管道和现代 C++ 的特性
-
你要求这么多痛苦!为什么不帮自己一个忙,参加
Vector课程? -
不,向量概念对我来说已经足够了:D 我实际上试图获得足够的经验和“高级模板”的舒适度,所以我以后可以尽可能少地完成 Stroustrup 书中的 N 维矩阵类(对我来说) 代码重复 :)
标签: c++ templates c++-concepts