美文网首页C++
C++20:概念之细节

C++20:概念之细节

作者: 奇点创客 | 来源:发表于2020-02-27 09:37 被阅读0次

    原文详见:C++20: Concepts, the Details

    在我的上一篇文章 C++20:两个极端和概念的救赎中,我给出了概念的第一个动机:概念对模板参数进行语义约束。今天,我将以紧凑的形式来介绍概念的不同用例。

    细节

    只要记住这一点:概念的优点是什么?

    • 约束(requires)将变为模板接口的一部分而存在
    • 函数的重载或类模板的特化可以基于概念
    • 因为编译器能够将模板参数的约束与实际模板参数相比较,从而使我们能得到更加友好的报错信息
    • auto 和概念的用法统一到了一起。你可以不使用 auto,而是使用概念
    • 如果函数声明使用了概念,它将自动成为函数模板。因此,编写函数模板与将与编写函数一样容易

    这篇文章是关于前三点的。让我们来看看概念的许多不同用法:

    三种方式

    有三种使用可排序概念的方式。为了简单起见,我仅显示函数模板的声明。

    制约条件
    template<typename Cont>
        requires Sortable<Cont>
    void sort(Cont& container);
    
    尾随制约条件
    template<typename Cont>
    void sort(Cont& container) requires Sortable<Cont>;
    
    受制约的模板参数
    template<Sortable Cont>
    void sort(Cont& container)
    

    在这种情况下,算法排序要求容器是可排序的。 Sortable 必须是一个常量表达式和一个谓词。

    你可以定义仅接受对象的类模板。

    template<Object T>
    class MyVector{};
    
    MyVector<int> v1;   // OK
    MyVector<int&> v2;  // ERROR: int& does not satisfy the constraint Object
    

    编译器会抱怨引用不是对象。也许您想知道,什么是对象? std::is_object 的实现 -- 类型萃取函数可能已经给出了答案:

    template< class T>
    struct is_object : std::integral_constant<bool,
                         std::is_scalar<T>::value ||
                         std::is_array<T>::value  ||
                         std::is_union<T>::value  ||
                         std::is_class<T>::value> {};
    

    对象可以是标量,也可以是数组,也可以是联合或类。

    成员函数

    template<Object T>
    class MyVector{
        ... 
        void push_back(const T& e) requires Copyable<T>{}
        ...
    };
    

    在这种情况下,成员函数要求模板参数T必须是可复制的(Copyable)。

    可变参数模板

    // allAnyNone.cpp
    
    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    concept Arithmetic = std::is_arithmetic<T>::value;
    
    template<Arithmetic... Args>
    bool all(Args... args) { return (... && args); }
    
    template<Arithmetic... Args>
    bool any(Args... args) { return (... || args); }
    
    template<Arithmetic... Args>
    bool none(Args... args) { return !(... || args); }
    
    int main(){
    
        std::cout << std::boolalpha << std::endl;
                   
        std::cout << "all(5, true, 5.5, false): " << all(5, true, 5.5, false) << std::endl;  
    
        std::cout << "any(5, true, 5.5, false): " << any(5, true, 5.5, false) << std::endl; 
                  
        std::cout << "none(5, true, 5.5, false): " << none(5, true, 5.5, false) << std::endl;     
        
    }
    

    你可以在可变参数模板中使用概念。函数模板的定义基于折叠表达式。all、any 和 none 都需要类型参数 T 来支持 Arithmetic(算术) 概念。Arithmetic 概念的本质意味着 T 不是整数就是浮点数。

    全新的 Microsoft 编译器 19.23 仅部分支持概念提案的语法。

    多个要求

    当然,你可以对模板参数使用多个要求。

    template <SequenceContainer S,   
              EqualityComparable<value_type<S>> T>
    Iterator_type<S> find(S&& seq, const T& val){
        ...
    }
    

    函数模板的 find 要求容器 S 是SequenceContainer(序列容器),并且其元素是EqualityComparable(可相等比较的)。

    重载

    std::advance(iter, n) 会增加给定的迭代器 iter 以 n 个元素的步长。根据迭代器的不同,实现可以使用指针算术,也可以依次执行 n 次增加。在第一种情况下,执行时间是常数;在第二种情况下,执行时间取决于步骤大小 n。由于概念,您可以针对迭代器的不同种类将 std::advance 重载。

    template<InputIterator I>
    void advance(I& iter, int n){...}
    
    template<BidirectionalIterator I>
    void advance(I& iter, int n){...}
    
    template<RandomAccessIterator I>
    void advance(I& iter, int n){...}
    
    // usage
    
    std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9};
    auto vecIt = vec.begin();
    std::advance(vecIt, 5);       //  RandomAccessIterator
    
    std::list<int> lst{1, 2, 3, 4, 5, 6, 7, 8, 9};
    auto lstIt = lst.begin();
    std::advance(lstIt, 5);       //  BidirectionalIterator
    
    std::forward_list<int> forw{1, 2, 3, 4, 5, 6, 7, 8, 9};
    auto forwIt = forw.begin();
    std::advance(forwIt, 5);      //  InputIterator
    

    根据迭代器种类的不同,对容器 std::vector,std::list 和 std :: forward_list 的支持将匹配到最合适的 std :: advance 实现。

    特化

    概念还支持模板特化

    template<typename T>
    class MyVector{};
    
    template<Object T>
    class MyVector{};
    
    MyVector<int> v1;     // Object T
    MyVector<int&> v2;    // typename T
    
    • MyVector<int&> 匹配到不受约束的模板参数
    • MyVector<int> 匹配到受约束的模板参数

    接下来?

    我的下一篇文章是关于 C++20 的语法统一。使用 C++20,您可以在每个地方使用受约束的占位符(concept),或者以 C++11 的方式使用不受约束的占位符(auto)。但这并不是统一的结束。用 C++20 定义模板变得非常简单,只需在函数的声明中使用带约束或不带约束的占位符即可。

    相关文章

      网友评论

        本文标题:C++20:概念之细节

        本文链接:https://www.haomeiwen.com/subject/xoegchtx.html