简短的问题是:如何使模板化函数的行为不同,基于 关于参数概念的支持。
我的意思是,如果类型 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相比,可以在这里应用哪些技术来使该代码对最终用户更清晰,并且有更好的方法来提供基于概念的参数多态性?
我能想到的唯一解决方案是使用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>();
...
答案 0 :(得分:0)
只需为要支持的所有类型重载运算符。您还可以添加另一个版本,该版本会产生可理解的编译时错误
Matrix& operator+=(Matrix const&);
template<typename matrix, typename=enable_if_t<is_matrix<matrix>::value>>
Matrix& operator+=(matrix const&);
template<typename scalar, typename=enable_if_t<is_scalar<scalar>::value>>
Matrix& operator+=(scalar);
template<typename arg>
Matrix& operator+=(arg&&) // catch attempts to add wrong argument type
{
static_assert(is_matrix<arg>::value || is_scalar<arg>::value,
"Matrix::operator+=(arg) requires matrix or scalar argument");
assert(false);
return*this;
}
您还可以声明最后一个运算符[[noreturn]]
。