C ++中的概念多态性

时间:2019-03-22 03:06:34

标签: c++ templates c++-concepts

简短的问题是:如何使模板化函数的行为不同,基于 关于参数概念的支持。

我的意思是,如果类型 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>();
...

1 个答案:

答案 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]]

相关问题