C++20 高级编程

c++20协程基础概念 c++协程 阅读详情

前言

  • C++20 是C++在C++11 之后最大的一次语言变革, 其中引入了大量具有革命性的新特性.
  • 本节包含了C++20中相当重要的四大特性: 概念约束, ranges(范围)标准库, 协程以及模块
    • 概念约束:
             是一个编译期谓词, 它根据程序员定义的接口规范对类型,常量等进行编译时检查,以便在泛型编程中为使用者提供更好的可读性与错误信息.
    • ranges标准库:
             对现有的标准库进行了补充,它以函数式编程范式进行编程, 将计算任务分解成一系列灵活的原子操作, 使得代码的正确性更容易推理.
    • 协程:
             是一种可挂起, 可恢复的通用函数, 它的切换开销是纳秒级的, 相对其他方案而言占用的资源极低, 并且可以非侵入式地为已有库扩展协程接口, 它常常用于并发编程, 生成器, 流处理, 异常处理等.
    • 模块:
             解决了传统的头文件编译模型的痛点: 依赖顺序导致头文件难以组合, 重复解析, 符号覆盖等问题, 从语言层面为程序员提供了模块化的手段.
  • 涉及了一些元编程的概念

前奏

lambda

  • lambda其实是由编译器生成的一个匿名类
  • 如果lambda包含在捕获列表内, 那么捕获将在对应的匿名类中生成成员变量与构造函数来存储捕获;
  • 对于无捕获的lambda而言, 其生成的匿名类中拥有一个非虚的函数指针类型转换操作符, 能够将lambda转换成函数指针. 这个不难理解, 因为无状态的 lambda 表达式可以赋给无状态的函数指针;
  • 在C++20 中泛型 lambda 也支持以模板参数形式提供, 这样就能保证两个形参类型一致.
    auto add=[]<typename T>(T a,T b){return a+b;};
    cout<<add(1,2)<<endl;
  • 模板函数只有实例化之后才能传递, 而泛型 lambda 是一个对象,可以按值传递, 在调用时根据实际传参进行实例化 模板函数 operator(),从而延迟了实例化的时机, 大大提高了灵活性. 标准库的一些算法通常要求对函数对象进行组合, 此时泛型 lambda 将能通过编译, 而模板函数不行.

浅谈std::ref的实现

  • 就是说构造一个新的对象(reference_wrapper是一个类模板)并在内部保存原来传入的变量的地址 _f 和类型 type
  • 重载类型转换为原来变量的引用类型, 有了_f 和 type, 这样可以在需要的时候自动将reference_wrapper转换为原来传入的变量实体的引用;
  • reference_wrapper 她本身可能会被 decay 但内部存储的 _f 值始终不会变, 始终可以在需要的时候自动转换为 type&
  • 我们可以自己简单实现一下:
    template<typename T>
    class my_ref{
    public:
        T * _f;
      explicit  my_ref(T& var): _f(addressof(var)){
    
        };
        operator T& () { return *_f; };
    //如果传入的是一个可调用对象
        template<class... Args>
        auto&& operator()(Args&& ...args){
    	 return (*_f)(args...);
        }
    };
    
  • 参考: https://zhuanlan.zhihu.com/p/581739392

浅谈is_same

  • 这是C++11引入的, 其实很简单, 模板特化就行了. 让编译器决定. 考虑: 万一我要运行时才能确定类型呢?
    template<class T, class U> struct is_same : std::false_type {};
    //偏特化版本 待确认一个模板参数
    template<class T> struct is_same<T, T> : std::true_type {};
    

浅谈std::function的实现

  • 有点类似上面的std::ref
  • 主要是对函数参数列表类型的获取, 可以使用模板特化来达到目的 C++ Template -> [5] -> 自由函数与模板可变参数

std::visit 与 std::variant 与运行时多态

  • 用法参考:
    https://zhuanlan.zhihu.com/p/676918348
    https://zhuanlan.zhihu.com/p/670189611
  • std::variant 行为像是一个类型安全的联合体。它存储了一系列类型,并能够在运行时安全地处理这些类型之一。
  • 它保留足够的空间来存储其可能的任何类型,通常是这些类型中最大者的大小。此外,std::variant 还需要额外的存储空间来跟踪当前存储的类型。
  • 为了维护类型安全,std::variant 使用一个内部索引来标记当前激活的类型。当访问或修改 std::variant 的值时,它会检查这个索引,并确保操作符合当前激活的类型。
  • std::visit 的第一个参数传入一个可调用对象,后面传入的是可调用对象的参数(可以用variant)。 std::visit 的工作原理依赖于编程语言或编译器的内部机制,这些机制通常对程序员透明。其中一种可能的实现方式是使用 “vtable”(Virtual Table,虚函数表)。 总之, std::visit 会在运行时查找函数地址。每当创建一个 std::variant 对象的时候,就会产生一个与之关联的 vtable,同来存储这个 std::variant 中存储的相关信息。
  • visit 编译时静态绑定 (第二个参数中各类型对应的第一个参数中可调用函数版本绑定到类型对应的索引 (用vtable来存储映射关系) )
  • visit 运行时动态绑定 (检查variant中当前激活的类型的索引). 从而决定要调用的函数版本
  • subtype多态例子:
    #include <iostream>
    #include <memory>
    #include <cmath>
    namespace Subtype
    {
        struct Shape
        {
            virtual ~Shape() = default;
            virtual double getArea() const = 0;
            virtual double getPerimeter() const = 0;
        };
        
        struct Circle: Shape
        {
            Circle(double r): r_(r) {}
            double getArea() const override
            {
                return M_PI * r_ * r_;
            }
            double getPerimeter() const override
            {
                return 2 * M_PI * r_;
            }
        private:
            double r_;
        };
        
        struct Rectangle: Shape
        {
            Rectangle(double w, double h): w_(w), h_(h) {}
            double getArea() const override
            {
                return w_ * h_;
            }
            double getPerimeter() const override
            {
                return 2 * (w_ + h_);
            }
        private:
            double w_;
            double h_;
        };
    }
    using namespace Subtype;
    
    int main(int argc, char** argv) {
        std::unique_ptr<Shape> shape = std::make_unique<Circle>(2);
        // shape area: 12.5664 perimeter: 12.5664
        std::cout << "shape area: " << shape->getArea()
            << " perimeter: " << shape->getPerimeter() << std::endl;
    
        shape = std::make_unique<Rectangle>(2, 3);
        // shape area: 6 perimeter: 10
        std::cout << "shape area: " << shape->getArea()
            << " perimeter: " << shape->getPerimeter() << std::endl;
    
        return 0;
    }
    
  • ad-hoc多态例子:
    #include <variant>
    #include <cmath>
    #include <iostream>
    namespace Adhoc
    {
        struct Circle
        {
            double r;
        };
        // Circle的一系列操作
        double getArea(const Circle& c)
        {
            return M_PI * c.r * c.r;
        }
        double getPerimeter(const Circle& c)
        {
            return 2 * M_PI * c.r;
        };
    
        struct Rectangle
        {
            double w;
            double h;
        };
        // Rectangle的一系列操作
        double getArea(const Rectangle& r)
        {
            return r.w * r.h;
        }
        double getPerimeter(const Rectangle& r)
        {
            return 2 * (r.w + r.h);
        };
    
        // 通过加法类型定义一个统一的类型Shape,其拥有不同的形状,从而实现运行时多态
        using Shape = std::variant<Circle, Rectangle>;
    
        // 统一类型Shape的一系列多态行为
        double getArea(const Shape& s)
        {
            return std::visit([](const auto & data)
            {
                return getArea(data);
            }, s);
        }
        double getPerimeter(const Shape& s)
        {
            return std::visit([](const auto & data)
            {
                return getPerimeter(data);
            }, s);
        };
    }
    using namespace Adhoc;
    
    int main(int argc, char** argv) {
        Shape shape = Circle{2};
        // shape area: 12.5664 perimeter: 12.5664
        std::cout << "shape area: " << getArea(shape)
            << " perimeter: " << getPerimeter(shape) << std::endl;
    
        shape = Rectangle{2, 3};
        // shape area: 6 perimeter: 10
        std::cout << "shape area: " << getArea(shape)
            << " perimeter: " << getPerimeter(shape) << std::endl;
        return 0;
    }
    
  • image

    subtype 多态和 ad-hoc 多态的表现形式对比

    多态形式定义多态调用
    subtype 多态Abstract* objobj->method()
    ad-hoc 多态Abstract objmethod(obj)

SFINAE

  • substitution failure is not an error

  • 典型的用法是利用enable_if
    enable_if:

    template<bool, typename _Tp = void>
      struct enable_if
      { };
    
    // Partial specialization for true.
    template<typename _Tp>
      struct enable_if<true, _Tp>//第二个参数默认为void
      { typedef _Tp type; };
    

    如果是false, 那么第一个空类没有type成员, 这时直接使用其type将报错
    但在SFINAE决策上下文环境中, 这种情况可以做为一种决策条件:

    下面情况下, 编译器如果根据实参推断发现enable_if<false>没有定义成员类型type, 将导致替换失败, 将其从候选集中删除, 从而达到我们的目的

    template<class T, enable_if_t<is_integral_v<T>>* = nullptr>
    void test(T t)
    {cout << "is integral" << endl;};
    
    template<class T, enable_if_t<is_floating_point_v<T>>* = nullptr>
    void test(T t)
    {cout << "is floating_point" << endl;};
    
  • Tips: 函数重载的过程中只看函数的声明, 如果它被决策为最佳可行函数, 但模板函数体内发生了模板参数替换失败, 那么就会在实例化过程中产生编译错误, 而不是SFINAE

  • Tips: SFINAE机制在其他条件相同情况下, 总是会优先选择更明确更具体的类型版本, 这也是模板特化能起作用的原因

类型内省

  • 检查对象的类型或属性的一种能力
  • 在C++中类型萃取也可以视作内省
  • 就能实现把各种类型各种分离与组合, <type_trait> 实现了这些功能
  • 比如 C++ Template -> [5] -> 自由函数与模板可变参数 中也是利用了类型内省 类似的还有数组 template<class E,size_t N> someArr<E[N]>{…}

标签分发 (tag dispatching)

  • 除了 enable_if 之外的编译时多态手段还有标签分发, 这也是C++社区著名的管用手法;

  • 标签常常是一个空类, 没有别的什么, 只是当作一种类型. 好让编译器在SFINAE决策中把它作为参考依据, 匹配出最合适的版本

  • 辅助类 true_type 和 false_type 类型也可视作标签, 他们把 true 和 false 包装成两种类型 这样在编译时就能决策出最合适的版本

  • 示例:

    template<class T>bool numEqImpl(T l, T r, true_type)
    {
        cout << "is floating" << endl;
        return true;
    }
    template<class T>bool numEqImpl(T l, T r, false_type)
    {
        cout << "is not floating" << endl;
        return false;
    }
    template<class T> enable_if_t<is_arithmetic_v<T>, bool> numEq(T l, T r)
    {
        return numEqImpl(l, r, is_floating_point<T> {});//标签分发
    }
    

    怎么样, 是不是逐渐理解一切了?

    标准库中在<iterator>中定义了如下迭代器标签
    input_iterator_tag;
    forward_iterator_tag;
    bidirectional_iterator_tag;
    random_access_iterator_tag;
    并且提供了配套的元函数 iterator_traits, 输入迭代器,输出相关属性;
    例如类型成员::iterator_category 存储的是迭代器的种类标签,::value_type 是解引用后的类型, ::difference_type 为迭代器作差后的类型 ptrdiff_t(通常是long类型的别名)

    iterator_traits<myIterator>::iterator_category{}实际调用时这样开始分发 ,有了标签就不用浪费内存真的去构造一个实体iterator对象了(大部分标签实现为空类)
    看看advance函数 它也利用了迭代器标签来实现

    也可以尝试利用 if constexpr 来实现advance

编译时多态

除了前面多多少少讲的一些编译时多态手段, 还有下面这些

  • 奇异递归模板模式 (Curiously Recurring Template Pattern,CRTP)

    • 代码复用: 由于子类派生于模板基类, 因此可以复用基类的方法
    • 编译时多态: 由于基类是一个模板类, 能够获得传递进来的派生类, 进而可以调用派生类的方法, 达到多态的效果. 与运行时多态相比没有虚表的开销.
    • 通常是如下形式:
     template<class T> struct Base{/*...*/};
     //派生类
     struct Derived : Base<Derived>{/*...*/};
    

    看一个利用 CRTP 形成静态多态的例子:

     #include <iostream>
     using namespace std;
     
     template<typename Derived>
     struct Animal {
         void bark() { static_cast<Derived&>(*this).barkImpl(); }
     };
     
     class Cat: public Animal<Cat> {
         friend Animal;
         void barkImpl() { cout << "Miaowing!" << endl; }
     };
     
     class Dog: public Animal<Dog> {
         friend Animal;
         void barkImpl() { cout << "Bow wow!" << endl; }
     };
     
     template<typename T>
     void play(Animal<T>& animal) { animal.bark(); }
     
     int main(int argc, char** argv) {
         Cat cat; play(cat); // Miaowing!
         Dog dog; play(dog); // Bow wow!
     
         return 0;
     }
    

    从play的实现来看, 它接受一个统一的抽象接口类, 而调用的时候根据实际类型是Cat还是Dog进行多态调用, 由于类型是在编译时确定的, 所以多态调用也是在编译时绑定的. 为何不把play写成模板函数的形式? 这样无需模板基类Animal<T>也能实现静态多态.
           原因在于CRTP的模板基类可以起到接口类的作用, 它明确列出了该派生体系中所支持的一组操作, 换句话说Animal<T>其实是对它所接受的类型,所支持的方法的集合进行约束, 而普通的模板函数对模板参数没有约束, 需要使用高级技巧(如 enable_if)进行约束.
           C++演进的过程中考虑了这一点, 在 C++20 中提供 concept 特性对模板参数进行约束.

    Visitor Pattern (访问者模式)
    虽然现代C++使用 std::variant 与 std::visit 来代替访问者模式
    但先辈们想出来的这个复合了奇异递归模板模式的模式…其思想…高内聚低耦合总是现代C++所追求的…

    点击查看代码
    #include <iostream>
    #include <string>
    #include <memory>
    #include <vector>
    #include <variant>
    
    using namespace std;
    
    struct VideoFile;
    struct TextFile;
    
    struct Visitor {
        virtual void visit(VideoFile&) = 0;
        virtual void visit(TextFile&) = 0;
        virtual ~Visitor() = default;
    };
    
    struct Elem {
        virtual void accept(Visitor& visit) = 0;
        virtual ~Elem() = default;
    };
    
    template<typename T>
    struct dump;
    
    template<typename Derived>
    struct AutoDispatchElem: Elem {
        void accept(Visitor& visitor) override
        { visitor.visit(static_cast<Derived&>(*this)); }
    };
    
    struct VideoFile: AutoDispatchElem<VideoFile> { /* ... */ };
    struct TextFile: AutoDispatchElem<TextFile> { /* ... */ };
    
    int main(int argc, char** argv) {
        std::vector<std::unique_ptr<Elem>> elems;
        elems.emplace_back(new VideoFile);
        elems.emplace_back(new TextFile);
    
        struct ConcreteVisitor: Visitor {
            void visit(VideoFile&) override
            { cout << "process VideoFile ..." << endl; }
            void visit(TextFile&) override
            { cout << "process TextFile ..." << endl; }
        } visitor;
    
        for (auto&& e: elems) e->accept(visitor);
    
        return 0;
    }
    
    

三路比较操作符 (飞船操作符) <=>

  • C++20 引入了三路比较操作符, 这样只需要实现一个直观的<=>操作符,就可以直接得到六个比较操作符<,==,!=,>,<=,>=
    image

    点击查看代码
    #include <iostream>
    #include <tuple>
    #include <cassert>
    
    using namespace std;
    
    namespace crtp {
    template<typename Derived>
    struct Comparable {
        friend bool operator==(const Derived& lhs, const Derived& rhs)
        { return lhs.tie() == rhs.tie(); }
        friend bool operator!=(const Derived& lhs, const Derived& rhs)
        { return !(lhs == rhs); }
    
        friend bool operator< (const Derived& lhs, const Derived& rhs)
        { return lhs.tie() < rhs.tie(); }
        friend bool operator> (const Derived& lhs, const Derived& rhs) { return rhs < lhs; }
        friend bool operator<=(const Derived& lhs, const Derived& rhs) { return !(lhs > rhs); }
        friend bool operator>=(const Derived& lhs, const Derived& rhs) { return !(lhs < rhs); }
    };
    
    struct Point: Comparable<Point> {
        Point(int x, int y): x(x), y(y) { }
        int x; int y;
        auto tie() const { return std::tie(x, y); }
    };
    }
    
    namespace classical {
    struct Point {
        int x; int y;
        friend bool operator==(const Point& lhs, const Point& rhs)
        { return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y); }
        friend bool operator<(const Point& lhs, const Point& rhs)
        { return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y); }
    
        friend bool operator!=(const Point& lhs, const Point& rhs) { return !(lhs == rhs); }
        friend bool operator> (const Point& lhs, const Point& rhs) { return rhs < lhs;     }
        friend bool operator<=(const Point& lhs, const Point& rhs) { return !(lhs > rhs);  }
        friend bool operator>=(const Point& lhs, const Point& rhs) { return !(lhs < rhs);  }
    };
    }
    
    namespace modern {
    struct Point { // C++20
        int x; int y;
        friend auto operator<=>(const Point& lhs, const Point& rhs) = default;
    };
    }
    
    #define PointTest() assert((Point{1,2} >= Point{1,0})); \
                        assert((Point{1,2} <= Point{5,0})); \
                        assert((Point{1,2} == Point{1,2})); \
                        assert((Point{1,2} != Point{1,3})); \
                        assert((Point{1,2} <  Point{1,3})); \
                        assert((Point{3,2} >  Point{1,3}))
    
    int main(int argc, char** argv) {
        {
            using crtp::Point;
            PointTest();
        }
    
        {
            using classical::Point;
            PointTest();
        }
    
        {
            using modern::Point;
            PointTest();
        }
    
        return 0;
    }
    
    

enable_shared_from_this 模板类

  • 异步编程中存在一种场景, 需要在类中将该类的对象注册到某个回调类或函数中, 不能简单的将this传到回调类中, 很可能因为回调时该对象被释放而导致野指针访问, 也不能直接将this构造成shared_ptr, 因为无法直接通过裸指针获得引用计数信息, 强行构造也会导致对象内存被多次释放.
    struct Obj {
        void submit() {
            executor.submit([this] { this->onComplete(); });//内存风险
        }
        void onComplete() {/*...*/}
    };
    
    那么如果获得裸指针(this) 的引用计数信息呢? 标准库提供了enable_shared_from_this 模板基类 通过CRTP在父类中存储子类的指针信息与引用计数信息, 并提供 shared_from_this 和 weak_from_this 以获得智能指针.
    struct Obj : enable_shared_from_this<Obj> {
        void submit() {
            executor.submit([obj = weak_from_this()] {
                if (auto spObj = obj.lock()) spObj->onComplete();
            });
        }
        void onComplete() {/*...*/}
    };
    
    为在类内处理它本身引用计数逻辑提供了便利

概念约束

  • C++20 起对 concept 特性进行了标准化, 主流的编译器也提供了支持
    将一类数据类型和对它的一组操作所满足的公理集称为 concept: 不仅需要从语法上满足要求, 还需要从语义层面上满足.
  • concept 拥有强大的表达力, 程序员能够非常简单的定义一个概念, 也可以对概念库中已有的概念进行组合. 概念支持重载, 能够消除对变通方案(enable_if等技巧)的依赖, 大大降低了元编程的难度, 也简化了泛型编程.

定义概念

  • 是一个对类型约束的编译期谓词, 给定一个类型判断其能否满足语法和语义要求, 这对泛型编程而言极为重要.比如, 给定模板参数T, 对它的要求如下:

    1. 一种迭代器类型 Iterator<T>
    2. 一种数字类型 Number<T>
      Iterator 和 Number 就是概念
  • 基本定义语法:

    template<被约束的模板参数列表>
    concept 概念名 = 约束表达式;

    概念和模板 using 的别名很类似, 前者是对布尔表达式的别名, 而后者是对模板类型的别名.
    约束表达式 可以简单理解为 布尔常量表达式
    如果在定义概念时约束表达式类型不为 bool 类型, 将引发一个编译错误, 而不是返回 false

    //Atomic constraint must be of type 'bool' (found 'int')
    template <typename T> concept Foo=1;
    

    >.GCC编译器定义concept时不会报错, 在求值的时候才进行类型检查;

  • 在概念标准库<concepts>中可以看到:

    template<typename T>
    concept integral = is_integral_v<T>;
    template<typename T>
    concept floating_point = is_floating_point_v<T>;
    

    简单使用展示:

    static_assert(floating_point<float>);
    static_assert(! floating_point<int>);
    
  • 约束表达式中使用 && 与 ||
    合取(conjunction)与析取(disjunction)操作与逻辑表达式中的与或运算类似, 也是一个短路操作符.

    template <typename T>
    concept Foo = is_integral_v<typename T::type>||sizeof(T)>1;
    static_assert(Foo<double>);//ok,因为 sizeof(T)>1 满足(true)
    
  • 原子约束
    在依次对每个条件检查时, 首先检查表达式是否合法, 若不合法则该约束不满足,返回false 否则进一步对约束进行求值判断是否满足.
    对可变模板参数形成的约束表达式, 既不是约束合取也不是约束析取

    //要求每个模板参数都含有类型成员type, 否则整个表达式为false,
    //因为原子约束先检查整个表达式是否合法, 如果有一个不合法, 那么整个表达式就为 false 
    //在整个表达式全部合法的前提下, 才有意义谈|| && 才进入求值判断阶段
    // || 表示有一个参数符合就返回true,&& 表示所有参数符合才返回true
    template <typename... Ts>
    concept Foo1 = (is_integral_v<typename Ts::type>||...);
    

    如果你仔细看看这个表达式结构…oh my gosh! 这可不就是个折叠表达式么? C++ Template >> [ 5 ].模板的可变参数->Variadic Expressions

    非可变模板参数如何形成原子约束?

    template <typename T>
    concept Foo = bool(is_integral_v<typename T::type>||sizeof(T)>1);//原子约束
    
  • 中间层
    上面有关可变模板参数的 concept 想要表达 “至少一个模板参数存在类型成员 type 且类型成员为整型” 怎么办? 很简单, 创建一个约束中间层就行了
    concept 的返回值总可能合法

    template <typename T>//中间层
    concept Midal = is_integral_v<typename T::type>;
    template <typename... Ts>
    concept Foo1 = (Midal<Ts> ||...);
    
  • negation (逻辑否定)

    template<typename T>concept C1 = is_integral_v<typename T::type>;
    template<typename T>concept C2 =! is_integral_v<typename T::type>;
    //T存在type且为整数类型时 C3 为 false, 反之只要C1中有一条不满足则C1为false, C3这时就为true
    template<typename T>concept C3 =! C1<T>;
    
    struct Foo{using type = float ;};
    //static_assert(C1<int>);//C1 表达: 要求T存在关联类型type,且 关联类型为整数类型
    //static_assert(C1<Foo>);
    static_assert(C2<Foo>);//C2 表达: 要求T存在关联类型type,且 关联类型不为整数类型
    static_assert(C3<Foo>);//C3 表达: 要求T不存在关联类型type,或 关联类型不为整数类型
    static_assert(C3<int>);//其实是把C1当作一个整体来看,只看结果
    

    注意两者语义的细微差别

requires 表达式

模板元编程

constexpr 元编程

Ranges 标准库

协程

软件设计六大原则 SOLID

  • Single responsibility principle
  • Open Closed Principle
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle
  • Dependence Inversion Principle
  • Law of Demeter
  • https://baike.baidu.com/starmap/view?nodeId=294b5d3c8cc7452ad5c9cdba&lemmaTitle=开闭原则&lemmaId=2828775&starMapFrom=lemma_starMap&fromModule=lemma_starMap

To be continue…

https://netcan.github.io/

C++20 协程(一) 协程是C++20最重要的特性,C++的异步编程模式有了协程将会变得简单。协程是能暂停执行以在之后恢复的函数。C++20协程是无栈的在堆上分配一个协程帧,把函数的参数拷贝到协程帧里,会在协程帧里创建promise对象。它们通过返回到调用方暂停执行,并且从栈分离存储恢复执行需要的数据。这样就可以编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持对惰性计算的无限序列上的算法及其他用途。 协程与函数 协程是一个有点特殊的函数,相比普通函数协程的特殊之处在于它可以暂停和恢复,如下图所示: 阅读详情

相关推荐

c++20快速解析-心得笔记【4】

enum

逻辑君的日常 243

C++20新特性概览

文章目录C++20概览C++20的重要性侧面说明:正面说明:模块(Modules)优点例子创建模块引用模块范围库(Ranges)Ranges 是什么 ?好处:相关概念例子概念库(Concepts)作用C++20以前C++20之后例子协程(Coroutines)协程概念相关关键字用处生成器并发库(Concurrency)原子智能指针智能指针(shared_ptr)线程安全吗?如何将智能指针变成线程安全?自动合并, 可中断的线程示例同步库(Synchronization)信号量(Semaphore)锁存器(La

weixin_43820463的博客 2万+

C++大学教程(第五版).[美]H.M.Deitel(带详细书签).pdf

本书是一本C++编程方面的优秀教程,全面介绍了过程式编程与面向对象编程的原理与方法,细致地分析了各种性能问题、可移植性问题以及可能出错的情况。与第四版相比,本书从内容和结构上都做了较大幅度的修订与有意义的提升,特别是再“尽早接触类和对象”这一观点的指导下,从第1章就开始介绍类和对象的内容,突破了传统教学模式,使学生直接“考虑对象”和深入掌握面向对象的基本概念。作者通过大量的示例程序,重点突出了利用UML进行面向对象的设计,引入了流行的Web应用开发,并且帮助学生创建真实世界的C++应用程序。本书无论从广度和深度上来说都非常全面,并且从基础概念讲起,同样适合没有编程经验的读者学习。 本书可作为高等院校相关专业的编程语言教材和C++编程教材,也是软件设计人员进行C++程序开发的宝贵参考资料。

现代C++编程:C++ 20新特性总结

概念是一种用于描述模板参数必须满足的条件或特性的机制。它们可以看作是模板参数的“类型约束”,让编译器在模板实例化时能够提前验证这些约束,从而提供更明确的错误提示和更强的编译时保障。协程是通过在函数返回类型前面添加co_awaitco_yield或co_return关键字来定义的。// 对范围进行一系列操作: 过滤、变换、排序和反转}) // 只保留偶数}) // 每个数值乘以3// 反转顺序// 打印结果return 0;

C/C++Linux、音视频、DPDK 8254

均匀分布产生高斯分布

均匀分布产生高斯分布 文章目录均匀分布产生高斯分布简介方法和证明代码效果引用 简介 方法和证明 代码 效果 引用

gy_Rick的博客 5317

c++2048小游戏编写

最近编写了一个小游戏,叫作2048,用到了面向对象的知识。

一个学生的博客 2371

探索C++20高级编程:新特性、技巧与性能优化

全书共10章,分别为:类型与对象、编译时多态、概念约束、元编程介绍、模板元编程、constexpr元编程、Ranges标准库、协程、模块、综合运用。其中C++20的四大特性独立成章,一些小的特性则贯穿于全书,其他章节则探讨了面向对象、元编程、函数式编程、并发编程等话题。作为一本讲解C++高级编程的书,本书还探讨了很多元编程话题,这是作为库开发必不可少的技能,它们也将随着C++的演进而不断演进,大大提升库开发者的编程体验,尤其是近年来C++的标准提案经历了从模板元编程向constexpr元编程转换的过程。

努力是为了站在万人之中,成为别人的光 1万+

无感无刷电机控制进阶:反电动势过零检测的5种实现方案对比

本文深入探讨无感无刷电机控制中反电动势过零检测的5种实现方案,包括电阻分压虚拟中性点法、专用BEMF检测IC方案、基于ADC采样的数字锁相环、无传感器观测器技术及混合方案创新实践。通过对比分析各方案的优劣及适用场景,为电机控制工程师提供实用的技术选型指南,助力提升系统性能与成本效益。

weixin_28691741的博客 843

C++20高级编程读书笔记

一种固定大小的数组,不会自动转换成指针,具有迭代器支持CTAD,可以这样定义。

c025200的博客 561

基于AIS动态数据与AI结合得经纬度标示算法

港口船舶智能标注系统 本系统通过结合摄像头视频流和AIS船舶动态数据,利用AI技术实现船舶实时标注功能。系统架构包括视频流处理、AIS数据解析、船舶检测匹配和坐标映射转换等模块。关键技术步骤: AIS数据获取:通过本地接收机或网络API获取船舶位置、航速等信息 AI目标检测:采用YOLOv8等模型识别视频中的船舶 船舶匹配算法:通过位置投影或运动轨迹匹配视频目标与AIS数据 坐标映射:使用相机标定技术将经纬度转换为图像坐标 实时标注:在视频画面上叠加船名、位置等关键信息 应用场景包括港口监控、智能海事管理和

qianbo042311的博客 1807

告别手动脚本!用TSMaster Python小程序5分钟搞定CAN报文自动发送

本文介绍了如何使用TSMaster的Python小程序功能快速实现CAN报文自动发送,大幅提升汽车电子测试效率。通过简洁的Python代码示例,展示了如何替代传统手动操作和复杂C脚本,实现周期性发送、动态修改报文内容等常见需求,帮助工程师5分钟内完成自动化配置。

weixin_30292843的博客 510

拥抱C++的深度和复杂性,挖掘更多可能 !——《C++20高级编程(第5版)》

C++难以掌握,但其广泛的功能使其成为游戏和商业软件应用程序中最常用的语言。即使是有经验的用户通常也不熟悉许多高级特性,但C++20的发布提供了探索该语言全部功能的绝佳机会。《C++20高级编程(第5版)》为C++的必要内容提供了一个代码密集型、面向解决方案的指南,包括最新版本中的最新工具和功能。本书包含面向现实世界编程的实用指导,是程序员深入研究C++的理想机会。第5版涵盖了C++20的内容。可以说本书的出版是 C++开发人员的福音,本书既适合新手学习 C++基础知识,又适合中高级开发者进阶。

清图出版 804

自动驾驶仿真工具链集成:ECU TEST与VTD、VERISTAND的深度配置指南

本文详细介绍了自动驾驶仿真测试中ECU TEST、VTD与VERISTAND三大核心工具的深度集成与连接配置方法。通过解析工具链的角色分工,提供从单机到分布式部署的实战指南,并重点阐述了Test Bench Configuration与Test Configuration的关键参数设置、SSH免密登录配置,以及在线与离线工作模式的切换技巧,旨在帮助测试工程师高效构建稳定、自动化的硬件在环测试环境。

wind6的博客 788

探索现代C++20编程艺术:高级C++20编程实践指南

探索现代C++20编程艺术:高级C++20编程实践指南 在计算机科学的世界里,C++一直是一个强大的、高度灵活的编程语言,尤其是在系统级编程和高性能计算领域。随着C++20标准的发布,这个语言更加强大且现代化。如果你正在寻找一个深入学习和应用C++20特性的资源,项目就是一个值得探索的宝藏。 项目简介 该项目由知名C++开发者Netcan发起,旨在帮助开发者充分利用C++20的新特性,提升编程技能...

gitblog_00078的博客 523

基于全局和局部接受性因果特征的广义学习模型,用于在线增量机械故障诊断

本文提出了一种**因果广义学习模型(CBLM)**,用于在线增量机械故障诊断。该模型结合了**全局因果特征**(时域、频域、故障特征频率均值)和**多尺度局部因果特征**(通过扩张卷积提取),构建了一个具有**增量学习能力**的广泛学习系统(BLS)。CBLM支持**结构增量学习**(增加增强节点)和**样本增量学习**(动态更新权重),无需重新训练整个模型,显著提高了计算效率和诊断精度。在两个滚动轴承数据集上的实验表明,CBLM在诊断准确性和训练时间上均优于传统深度学习模型,适用于在线实时故障诊断。

huanghm88的专栏 387

高精度地图数据的结构

本文是个人在学习高精度地图数据结构的心得,主要是描述高精度地图的几层数据结构以及元数据信息。

qq_40664711的博客 5575

C++高级编程(2)-- 深入C++20新特性

C++20,作为C++标准的最新迭代,为我们带来了一系列激动人心的新特性。这些特性不仅提高了开发效率、增强了代码安全性,而且让代码更加简洁易读。接下来,让我们深入了解C++20的一些关键新特性,并且探讨它们的高级用法和示例代码。

m0_51768024的博客 1564

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

C++20 高级编程 004:从初始化、内存到const系列关键字

不少深耕 C++ 的开发者常会陷入一种尴尬:能写业务逻辑,却在底层语法细节栽跟头 —— 指派初始化分不清、栈堆内存乱释放、const指针看得头皮发麻、constexpr与consteval傻傻分不清。本篇结合《C++20 高级编程(第 5 版)上册》第一章完整知识点,把书中原文重点提炼,搭配可直接运行的代码示例,兼顾趣味性与实用性,帮你一次性吃透底层核心逻辑,告别模糊认知~全文知识点全部取自《C++20 高级编程(第 5 版)上册》第一章原文,没有脱离标准教材的基础语法拓展。

软件科学-郝学胜 4007

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

【C++高级开发应用篇】探索C++20中的协程:异步编程的强大工具

【C++高级开发应用篇】探索C++20中的协程:异步编程的强大工具

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 2467

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【C++20编译期编程突破】:constexpr构造函数初始化的5种高级用法

掌握C++ constexpr 构造函数的初始化技巧,提升编译期计算能力。涵盖字面量类型构造、模板元编程、数组预计算等5种高级用法,适用于高性能与零成本抽象场景。代码更安全高效,值得收藏。

CompiTide的博客 403

【风靡全球年近40年的C++过时了吗?C++20来告诉大家】

作为带有类的C的继承者,丹麦计算机科学家Bjarne Stroustrup于1982年发明了C++。1985年,发布了第一版的“C++程序设计语言”。第一个标准化版本的C++在1998年发布,称为C++98。在2003年,C++03发布并包含了一些小的更新。在那之后,C++沉默了一段时间,但吸引力开始慢慢增强,导致该语言在2011年进行了重大更新,称为C++11。从那以后,C++标准委员会以3年的周期发布更新的版本,出现了C++14、C++17以及现在的C++20。总之,2020年发布了C++20之后,C+

清图出版 3027

C++用户组的创始人倾情分享——《C++20高级编程(第5版)》

清图出版 767

C++网络编程:高级通信协议与异步处理

本文介绍了C++网络编程中的高级通信协议与异步处理技术。主要内容包括:HTTP/HTTPS/WebSocket协议原理与实现,使用cpp-httplib库开发HTTP服务器、HTTPS安全通信和WebSocket实时通信;网络库Boost.Beast和cpp-httplib的应用实践;异步编程概念与协程技术,通过示例展示异步网络通信的实现方法。文章提供了多个实用代码示例,帮助开发者掌握高性能网络应用开发的关键技术。

编程世界如山海,探索不止。 7609

C++20 高级编程 002:夯实现代C++底层基本功

本文介绍了C++20的核心语法特性,帮助开发者掌握现代C++编程技巧。主要内容包括: 模块化编程:C++20的模块系统替代传统头文件,解决宏污染和编译缓慢问题,通过export控制可见性。 分支控制:详细讲解if/else和switch语句,介绍C++17引入的初始化器语法,减少变量污染作用域。 运算符:涵盖三元条件运算符、逻辑运算符的短路特性,以及C++20新增的太空飞船运算符<=>,简化比较逻辑。

软件科学-郝学胜 576

C++20 高级编程 003:吃透现代基础语法与标准容器

本文介绍了现代C++(C++11至C++20)中的核心语法与标准容器,旨在帮助开发者摒弃传统C风格编程习惯。

软件科学-郝学胜 3858
上一篇: std::declval 元函数
下一篇: Start gtkmm 4 Programming (range controls)_
Computer_Tech
博客等级 码龄3年 573粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值