C++ 元编程

目录

  • C++ 元编程
    • 1. 术语
    • 2. 元函数
      • 1. 数值元函数
        • 示例:阶乘计算
      • 2. 类型元函数
        • 示例:类型选择
    • 3. 混合编程
      • 1. 常规的计算点积范例
      • 2. 混合元编程计算点积
    • 4. typelist实现
      • 设计和基本操作接口(算法)
      • 完整代码
    • 5. tuple 实现
      • 基础知识
        • 1. 左值、右值、左值引用、右值引用
        • 2. std::move 究竟做了什么
        • 3. std::forward 究竟做了什么
        • 4. 万能引用(转发引用)
        • 5. 完美转发
      • tuple 实现示例

C++ 元编程

元编程的主要目的在于将各种计算从运行期提前至编译期进行,以实现程序运行时的性能提升。也正因如此,元编程是一种增加程序的编译时间,从而提升程序运行效率的编程技术。

在元编程中,涉及许多与循环相关的代码。传统编程中,循环通常采用 forwhile 等语句实现,这些语句一般针对的是运行期的条件变量;而在元编程中,更多的操作其实是针对类型或常量的。这种循环的实现往往会采用递归的手段,使得编译器能够在编译期间完成某些计算。

1. 术语

元编程的英文名称是 Meta Programming,有时也称为 模板元编程(Template Metaprogramming)。可以理解为一种编程手法,用于实现一些比较特殊的功能。元编程与“递归”这一概念紧密相连,代表着在元编程中,大多数情况下都会使用递归编程技术。

模板编程主要应用在泛型编程元编程

泛型编程(Generic Programming)强调的是“通用”的概念,旨在通过抽象和参数化来实现代码的复用与灵活性。在泛型编程中,程序员可以编写与类型无关的算法和数据结构,使得同一段代码能够适用于多种类型。模板的设计初衷正是为了满足这一需求。

元编程(Meta Programming)则是一种更高级的编程技巧,旨在通过编译期间的计算和推导来实现某些功能。元编程允许程序员在编译期进行复杂的逻辑处理,通常这些逻辑在运行时才能完成。

2. 元函数

传统的函数都是在程序运行期间被调用和执行的函数,而元函数是能在程序编译期间被调用和执行的函数(编译期间就能得到结果)。引入元函数概念的目的是支持元编程,而元编程的核心也正是元函数。

1. 数值元函数

数值元函数是在编译期间对数值进行计算的元函数。这类元函数可以接收整型常量作为模板参数,并根据这些常量进行编译时计算。数值元函数通常用于实现一些数学运算,如阶乘、斐波那契数列等。

示例:阶乘计算
template<int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

// 使用
constexpr int fact5 = Factorial<5>::value; // fact5 的值为 120
std::cout << fact5 << std::endl; // 输出 120

2. 类型元函数

类型元函数则是针对类型进行操作的元函数。它们允许程序员在编译期间对类型进行推导、选择和变换。这类元函数非常适合于类型特征提取和类型转换等场景。

示例:类型选择
template<bool Condition, typename TrueType, typename FalseType>
struct Conditional;

template<typename TrueType, typename FalseType>
struct Conditional<true, TrueType, FalseType> {
    using type = TrueType;
};

template<typename TrueType, typename FalseType>
struct Conditional<false, TrueType, FalseType> {
    using type = FalseType;
};

// 使用
using ResultType = Conditional<true, int, double>::type; // ResultType 的类型为 int

3. 混合编程

混合编程是结合了运行时和编译时计算的技术,允许程序员在编译期间进行一些运算,同时时间复杂度也得到优化。混合编程的关键在于利用模板和递归来处理类型和数值,使得某些操作可以在编译期完成,从而提高运行时性能。

1. 常规的计算点积范例

混合元编程方面,一个比较典型的案例是计算两个向量(数组)点积。

(1)数组a有3个元素a[0]、a[1]、a[2],值分别为1、2、3;

(2)数组b有3个元素b[0]、b[1]、b[2],值分别为4、5、6;

(3)a和b的点积是一个数值,结果为a[0]×b[0] + a[1] ×b[1] + a[2] ×b[2] =1×4+2×5+3×6=32。

在传统编程中,计算两个数组(向量)的点积通常使用循环结构,如forwhile。以下是一个简单的点积计算示例:

#include <iostream>

template<typename T, int U>
auto DotProduct(T* array1, T* array2)
{
    T dpresult = T{};
    for (int i = 0; i < U; ++i)
    {
        dpresult += array1[i] * array2[i];
    }
    return dpresult;
}

int main()
{
    int a[] = { 1,2,3 };
    int b[] = { 4,5,6 };
    int result = DotProduct<int, 3>(a, b);
    std::cout << result << std::endl; // 输出 32
    return 0;
}

2. 混合元编程计算点积

通过元编程,可以在编译期间计算点积。下面的代码展示了如何使用模板和递归实现这一点:

#include <iostream>

//泛化版本
template<typename T, int U> //T:元素类型,U:数组大小
struct DotProduct
{
    static T result(const T* a, const T* b)
    {
        return (*a) * (*b) +DotProduct<T, U - 1>::result(a + 1, b + 1);
    }
};
//特化版本,作为递归调用的出口
template<typename T>
struct DotProduct<T, 0>
{
    static T result(const T *,const T*)
    {
        return T{};
    }
};

int main()
{
    int a[] = { 1,2,3 };
    int b[] = { 4,5,6 };
    int result = DotProduct<int, 3>::result(a, b);
    std::cout << result << std::endl;
    return 0;
}
  1. 泛化版本:
    • DotProduct 是一个模板类,它接受元素类型 T 和数组大小 U 作为模板参数。
    • result 函数是一个静态成员函数,通过递归调用来计算点积。它取当前元素 *a*b 的乘积,并加上对下一个元素的递归调用。
  2. 特化版本:
    • U0 时,递归调用的出口到达。此时返回一个默认初始化的值 T{},表示点积计算的结束。

4. typelist实现

typelist的解释为:用来操作一大堆类型的C++容器,就像C++标准库中的list容器能够为数值提供各种基本操作一样(只不过这里操作的不是数值,而是类型)。

从实现上来讲,typelist是一个类模板,译为“类型列表”,这个类模板用来表示一个列表,这个列表中存放着一堆类型。

设计和基本操作接口(算法)

Typelist 的基本定义

template<typename... Types>
struct TypeList {};

// 基本类型操作
using EmptyTypeList = TypeList<>; // 空类型列表

1. 取得typelist中的第1个元素(front)

这个操作返回 typelist 中的第一个类型。通过模板特化,可以在编译时获取到这个类型。该操作通常用于获取类型链的起始类型,以便后续处理。

template<typename TList>
struct Front;

template<typename Head, typename... Tail>
struct Front<TypeList<Head, Tail...>> {
    using type = Head; // 返回第一个元素
};

2. 取得typelist容器中元素的数量(size)

这个操作计算并返回 typelist 中包含的类型数量。它使用可变参数模板的特性,通过 sizeof... 来计算类型的数量。

template<typename TList>
struct Size;

template<typename... Types>
struct Size<TypeList<Types...>> {
    static const size_t value = sizeof...(Types); // 元素计数
};

3. 从typelist中移除第1个元素(pop_front)

此操作会返回一个新的 typelist,该列表中不再包含第一个类型。它通过递归去掉开头的类型,为后续操作提供了简化的列表。

template<typename TList>
struct PopFront;

template<typename Head, typename... Tail>
struct PopFront<TypeList<Head, Tail...>> {
    using type = TypeList<Tail...>; // 移除第一个元素
};

4. 向typelist的开头和结尾插入一个元素(push_front和push_back)

  • push_front: 将新类型插入到 typelist 的开头,产生一个新的 typelist
  • push_back: 将新类型插入到 typelist 的结尾,同样产生一个新的 typelist
template<typename TList, typename NewType>
struct PushFront;

template<typename... Types, typename NewType>
struct PushFront<TypeList<Types...>, NewType> {
    using type = TypeList<NewType, Types...>; // 插入到开头
};

template<typename TList, typename NewType>
struct PushBack;

template<typename... Types, typename NewType>
struct PushBack<TypeList<Types...>, NewType> {
    using type = TypeList<Types..., NewType>; // 插入到结尾
};

5. 替换typelist的开头元素(replace_front)

此操作允许替换 typelist 的第一个类型为一个新的类型,并返回一个新的 typelist。这对于动态更改类型列表的开头非常有用。

template<typename TList, typename NewType>
struct ReplaceFront;

template<typename... Tail, typename NewType>
struct ReplaceFront<TypeList<Tail...>, NewType> {
    using type = TypeList<NewType, Tail...>; // 替换开头元素
};

6. 判断typelist是否为空(is_empty)

此操作检查 typelist 是否包含任何类型。如果列表为空,返回 true,否则返回 false。这是确保操作安全性的重要步骤。

template<typename TList>
struct IsEmpty;

template<>
struct IsEmpty<EmptyTypeList> {
    static const bool value = true; // 空类型列表
};

template<typename... Types>
struct IsEmpty<TypeList<Types...>> {
    static const bool value = false; // 非空类型列表
};

7. 根据索引号查找typelist的某个元素(find)

此操作通过索引查找 typelist 中的特定类型。它使用递归进行索引减小,直到找到目标索引的类型。这使得可以根据位置快速访问特定类型。

template<typename TList, size_t Index>
struct Find;

template<typename Head, typename... Tail>
struct Find<TypeList<Head, Tail...>, 0> {
    using type = Head; // 找到第一个元素
};

template<typename Head, typename... Tail, size_t Index>
struct Find<TypeList<Head, Tail...>, Index> {
    using type = typename Find<TypeList<Tail...>, Index - 1>::type; // 递归查找下一个元素
};

8. 遍历typelist找到sizeof值最大的元素(get_maxsize_type)

该操作遍历 typelist 中的所有类型,并返回 sizeof 值最大的类型。它利用条件选择,递归比较每个类型的大小,确定最大值。这在需要根据类型大小进行处理时非常有用。

template<typename TList>
struct GetMaxSizeType;

template<typename Head, typename... Tail>
struct GetMaxSizeType<TypeList<Head, Tail...>> {
    using type = typename std::conditional<
        (sizeof(Head) >= sizeof(typename GetMaxSizeType<TypeList<Tail...>>::type)),
        Head,
        typename GetMaxSizeType<TypeList<Tail...>>::type
    >::type; // 使用 std::conditional 比较
};

// 特化版本,处理空类型列表
template<>
struct GetMaxSizeType<EmptyTypeList> {
    using type = void; // 空类型列表没有最大类型
};

完整代码

#include <iostream>
#include <type_traits>

template<typename... Types>
struct TypeList {};

using EmptyTypeList = TypeList<>;

// 取得第一个元素
template<typename TList>
struct Front;

template<typename Head, typename... Tail>
struct Front<TypeList<Head, Tail...>> {
    using type = Head;
};

// 取得元素数量
template<typename TList>
struct Size;

template<typename... Types>
struct Size<TypeList<Types...>> {
    static const size_t value = sizeof...(Types);
};

// 移除第一个元素
template<typename TList>
struct PopFront;

template<typename Head, typename... Tail>
struct PopFront<TypeList<Head, Tail...>> {
    using type = TypeList<Tail...>;
};

// 向开头插入一个元素
template<typename TList, typename NewType>
struct PushFront;

template<typename... Types, typename NewType>
struct PushFront<TypeList<Types...>, NewType> {
    using type = TypeList<NewType, Types...>;
};

// 向结尾插入一个元素
template<typename TList, typename NewType>
struct PushBack;

template<typename... Types, typename NewType>
struct PushBack<TypeList<Types...>, NewType> {
    using type = TypeList<Types..., NewType>;
};

// 替换开头元素
template<typename TList, typename NewType>
struct ReplaceFront;

template<typename... Tail, typename NewType>
struct ReplaceFront<TypeList<Tail...>, NewType> {
    using type = TypeList<NewType, Tail...>;
};

// 判断是否为空
template<typename TList>
struct IsEmpty;

template<>
struct IsEmpty<EmptyTypeList> {
    static const bool value = true;
};

template<typename... Types>
struct IsEmpty<TypeList<Types...>> {
    static const bool value = false;
};

// 根据索引查找元素
template<typename TList, size_t Index>
struct Find;

template<typename Head, typename... Tail>
struct Find<TypeList<Head, Tail...>, 0> {
    using type = Head; // 找到第一个元素
};

template<typename Head, typename... Tail, size_t Index>
struct Find<TypeList<Head, Tail...>, Index> {
    using type = typename Find<TypeList<Tail...>, Index - 1>::type; // 递归查找下一个元素
};

// 遍历找到最大 sizeof 的类型
template<typename TList>
struct GetMaxSizeType;

template<typename Head, typename... Tail>
struct GetMaxSizeType<TypeList<Head, Tail...>> {
    using type = typename std::conditional<
        (sizeof(Head) >= sizeof(typename GetMaxSizeType<TypeList<Tail...>>::type)),
        Head,
        typename GetMaxSizeType<TypeList<Tail...>>::type
        >::type; // 使用 std::conditional 比较
};

// 特化版本,处理空类型列表
template<>
struct GetMaxSizeType<EmptyTypeList> {
    using type = void; // 空类型列表没有最大类型
};

// 测试
int main() {
    using MyTypes = TypeList<int, double, char>;

    std::cout << "Size: " << Size<MyTypes>::value << std::endl; // 输出 3
    std::cout << "Is empty: " << IsEmpty<MyTypes>::value << std::endl; // 输出 0
    std::cout << "First element type: " << typeid(Front<MyTypes>::type).name() << std::endl; // 输出 int

    using Popped = typename PopFront<MyTypes>::type;
    std::cout << "Size after pop: " << Size<Popped>::value << std::endl; // 输出 2

    using PushedFront = typename PushFront<MyTypes, float>::type;
    std::cout << "Size after push front: " << Size<PushedFront>::value << std::endl; // 输出 4

    using PushedBack = typename PushBack<MyTypes, long>::type;
    std::cout << "Size after push back: " << Size<PushedBack>::value << std::endl; // 输出 4

    using FoundType = typename Find<MyTypes, 1>::type; // 查找索引1的类型
    std::cout << "Found type at index 1: " << typeid(FoundType).name() << std::endl; // 输出 double

    using MaxSizeType = typename GetMaxSizeType<MyTypes>::type; // 查找最大 sizeof 的类型
    std::cout << "Max size type: " << typeid(MaxSizeType).name() << std::endl; // 输出 double

    return 0;
}
  • TypeList: 用于存储类型的模板类。
  • 基本操作: 提供了如获取第一个元素、计算大小、移除元素、插入元素、替换元素和判断是否为空等基本操作。
  • Find: 通过递归查找指定索引的类型。
  • GetMaxSizeType: 通过比较各类型的 sizeof 值,找出最大类型。
  • 使用示例: 在 main 函数中演示了如何使用这些操作。

5. tuple 实现

基础知识

1. 左值、右值、左值引用、右值引用
  • 左值和右值:

    • 左值 (lvalue): 表示一个可以被取地址的对象,具有持久的内存位置。例子:int i = 10; 这里的 i 是左值。
    • 右值 (rvalue): 表示一个临时的对象,通常是不能取地址的,存在于表达式的右边。例子:10 是右值。
  • 左值引用:

    • 通过左值引用 (&) 可以引用一个左值。例子:
      int &j = i; // j 是左值引用
      
    • 左值引用只能绑定到左值上。尝试将右值绑定到左值引用会导致编译错误,但 const 左值引用可以绑定到右值:
      const int &j = 10; // 合法
      
  • 右值引用:

    • 右值引用使用 && 进行声明,允许绑定到右值。
    • 例如:
      int &&k = 10; // k 是右值引用
      
    • 右值引用只能绑定到右值。尝试将左值绑定到右值引用会导致编译错误。
  • 普通变量:

    • 普通的变量(如 int m;)既不是左值引用也不是右值引用,因为引用必须带有 &&& 修饰符。
2. std::move 究竟做了什么
  • std::move 是一个标准库函数,它将左值转换为右值,从而允许使用右值引用。例如:

    int &&k = std::move(i); // 将左值 i 转换为右值
    
  • 重要的是,std::move 不会执行任何移动操作,它只是一种类型转换,标记一个对象可以被“移动”。

3. std::forward 究竟做了什么
  • std::forward 是用于实现完美转发的工具,它允许保持参数的值类别(lvalue 或 rvalue)。在模板中使用时,可以根据传入的参数类型决定是保持为左值还是右值。

    template<typename T>
    void func(T&& arg) {
        // 完美转发
        process(std::forward<T>(arg));
    }
    
4. 万能引用(转发引用)
  • 万能引用是指在模板参数中使用的引用类型,可以绑定到左值或右值(例如 T&&,其中 T 是模板参数)。这种引用在模板中被称为转发引用。
5. 完美转发
  • 完美转发使得函数可以根据实际传入的参数类型选择合适的引用类型,从而避免不必要的复制和保证性能。例如,结合 std::forward 和万能引用,可以实现完美转发。

tuple 实现示例

C++ 标准库中的 std::tuple 是一个可以存储不同类型的元素的容器,以下是一个简单的自定义 tuple 实现示例:

  • tuple 是一种灵活的数据结构,能够存储不同类型的元素。理解左值、右值及其引用非常重要,因为 tuple 的实现涉及到对象的生命周期管理、内存效率以及函数调用方式(如移动语义和转发)。
  • std::movestd::forward 是实现高效代码的重要工具,特别是在模板编程和泛型编程中。
#include <iostream>
#include <utility>
#include <type_traits>

template<typename... Types>
class MyTuple;

// 特化基础情况
template<>
class MyTuple<> {};

// 递归定义
template<typename Head, typename... Tail>
class MyTuple<Head, Tail...> : private MyTuple<Tail...> {
public:
    Head head; // 当前元素
    using MyTuple<Tail...>::head; // 继承下一层的头部元素

    MyTuple(Head h, Tail... t)
        : head(h), MyTuple<Tail...>(t...) {} // 构造函数
};

// 获取元素
template<size_t Index, typename Tuple>
struct TupleElement;

template<typename Head, typename... Tail>
struct TupleElement<0, MyTuple<Head, Tail...>> {
    using type = Head; // 返回当前头部元素
};

template<size_t Index, typename Head, typename... Tail>
struct TupleElement<Index, MyTuple<Head, Tail...>> {
    using type = typename TupleElement<Index - 1, MyTuple<Tail...>>::type; // 递归获取
};

// 获取元素的辅助函数
template<size_t Index, typename... Types>
typename TupleElement<Index, MyTuple<Types...>>::type& get(MyTuple<Types...>& tuple) {
    return static_cast<typename TupleElement<Index, MyTuple<Types...>>::type&>(tuple);
}

int main() {
    MyTuple<int, double, char> myTuple(1, 2.5, 'c');

    std::cout << "First element: " << get<0>(myTuple) << std::endl; // 输出 1
    std::cout << "Second element: " << get<1>(myTuple) << std::endl; // 输出 2.5
    std::cout << "Third element: " << get<2>(myTuple) << std::endl; // 输出 c

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/879717.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ARM】中断的处理

ARM的异常向量表 如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL0&#xff0c;那么使用第一组异常向量表。如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL1/2/3&#xff0c;那么使用第…

支付宝开发者✖️「蚂小财」——AgentUniverse专业多智能体框架在严谨产业中的应用实践

正在直播&#xff1a;点击进入直播间互动拿蚂蚁保温杯 &#xfeff;直播&#xfeff; &#xfeff;

英飞凌最新AURIX™TC4x芯片介绍

概述: 英飞凌推出最新的AURIX™TC4x系列,突破了电动汽车、ADAS、汽车e/e架构和边缘应用人工智能(AI)的界限。这一代面向未来的微控制器将有助于克服安全可靠的处理性能和效率方面的限制。客户将可缩短快速上市时间并降低整体系统成本。为何它被称为汽车市场新出现的主要颠覆…

828华为云征文 | 华为云Flexusx与Docker技术融合,打造个性化WizNote服务

前言 华为云Flexus X实例携手Docker技术&#xff0c;创新融合打造高效个性化WizNote服务。华为云Flexus X实例的柔性算力与Docker的容器化优势相结合&#xff0c;实现资源灵活配置与性能优化&#xff0c;助力企业轻松构建稳定、高效的云端笔记平台。828华为云企业上云节特惠来袭…

[2025]医院健康陪诊系统(源码+定制+服务)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Element UI入门笔记(个人向)

Element UI入门笔记 将页面分割为一级菜单、二级菜单、导航栏三个部分&#xff1b;使用npm下载安装&#xff0c;使用语句npm i element-ui -s; 布局组件 el-form 用于创建和管理表单&#xff1b;从属性上看&#xff1a; :model&#xff1a;用于双向数据绑定&#xff0c;将表单…

Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)

题目&#xff1a;*18.29(某个目录下的文件数目) 编写一个程序&#xff0c;提示用户输入一个目录&#xff0c;然后显示该目录下的文件数。 和上一题(18.28)的思路差不多&#xff0c;把找到文件后累加大小到变量变成计数1即可。 Java语言程序设计基础篇_编程练习题*18.28 (非递…

Linux(6)--CentOS目录

文章目录 1. 根目录2. cd目录切换命令3. CentOS目录介绍4. pwd命令介绍5. ls命令介绍5.1 ls5.2 ls -a5.3 ls -l 1. 根目录 Windows电脑的根目录是计算机(我的电脑)&#xff0c;然后C盘、D盘。 Linux系统的根目录是/&#xff0c;我们可以使用cd /进入根目录&#xff0c;然后使…

《深入理解JAVA虚拟机(第2版)》- 第12章 - 学习笔记

第12章 Java内存模型与线程 12.1 概述 TPS是用来衡量一个服务性能好坏高低的重要指标值。TPS是Transactions Per Second的缩写&#xff0c;用来表示每秒事务处理数&#xff0c;即服务端每秒平均能碰响应的请求数。 12.2 硬件的效率与一致性 处理器与内存的运算效率差了好几…

关于STM32项目面试题02:ADC与DAC篇(输入部分NTC、AV:0-5V、AI:4-20mA和DAC的两个引脚)

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;什么是模数转换/ADC&#xff1f;说说模数转换的流程&#xff1f; …

数字自然资源领域的实现路径

在数字化浪潮的推动下&#xff0c;自然资源的管理与利用正经历着前所未有的变革。本文将从测绘地理信息与遥感专业的角度&#xff0c;深度分析数字自然资源领域的实现路径。 1. 基础数据的数字化 数字自然资源的构建&#xff0c;首先需要实现基础数据的数字化。这包括地形地貌…

【速成Redis】02 Redis 五大基本数据类型常用命令

前言&#xff1a; 上一节课&#xff0c;我们对redis进行了初步了解&#xff0c;和安装好了redis。【速成Redis】01 Redis简介及windows上如何安装redishttps://blog.csdn.net/weixin_71246590/article/details/142319358?spm1001.2014.3001.5501 该篇博客&#xff0c;我们正…

八股文-JVM

是什么&#xff1f;有什么用&#xff1f;谁发明的&#xff1f;什么时候发明的&#xff1f; Java虚拟机&#xff0c;用来运行Java程序&#xff0c;有很多个版本的虚拟机&#xff0c;比如HotSpot&#xff0c;最开始是SUN公司开发人员&#xff0c;和Java一起发布&#xff0c;现在…

9. 什么是 Beam Search?深入理解模型生成策略

是不是总感觉很熟悉&#xff1f;Beam Search 是生成任务中常用的一种方法。 在之前第5&#xff0c;7&#xff0c;8篇文章中&#xff0c;我们都曾经用到过与它相关的参数&#xff0c;而对于早就有着实操经验的同学们&#xff0c;想必见到的更多。这篇文章将从示例到数学原理和代…

【C语言二级考试】循环结构设计

C语言二级考试——循环结构程序设计 五.循环结构程序设计 1.for循环结构 2.while和do-while循环结构 3.continue语句和break语句 4.循环的嵌套 知识点参考【C语言】循环-CSDN博客 文章目录 1.for循环2.while和do-while循环结构3.continue语句和break语句4.循环的嵌套 1.for循环…

智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!

文章目录 1 效果展示2 CogVideoX 前世今生3 CogVideoX 部署实践流程3.1 创建丹摩实例3.2 配置环境和依赖3.3 模型与配置文件3.4 运行4 遇到问题 1 效果展示 A street artist, clad in a worn-out denim jacket and a colorful bandana, stands before a vast concrete wall in …

论文速递!时序预测!DCSDNet:双卷积季节性分解网络,应用于天然气消费预测过程

本期推文将介绍一种新的时序预测方法:双卷积季节性分解网络&#xff08;Dual Convolution withSeasonal Decomposition Network, DCSDNet&#xff09;在天然气消费预测的应用&#xff0c;这项研究发表于《Applied Energy》期刊。 针对天然气消费的多重季节性和非规律性&#x…

C++ —— 关于vector

目录 链接 1. vector的定义 2. vector的构造 3. vector 的遍历 4. vector 的扩容机制 5. vector 的空间接口 5.1 resize 接口 5.2 push_back 5.3 insert 5.4 erase 5.5 流插入与流提取 vector 并不支持流插入与流提取&#xff0c;但是可以自己设计&#xff0c;更…

标准库标头 <barrier>(C++20)学习

此头文件是线程支持库的一部分。 类模板 std::barrier 提供一种线程协调机制&#xff0c;阻塞已知大小的线程组直至该组中的所有线程到达该屏障。不同于 std::latch&#xff0c;屏障是可重用的&#xff1a;一旦到达的线程组被解除阻塞&#xff0c;即可重用同一屏障。与 std::l…

基于SpringBoot项目实现Docker容器化部署

将Spring Boot项目部署到Docker容器中的涉及几个主要步骤&#xff1a; 准备Docker镜像 首先&#xff0c;需要选择一个基础镜像&#xff0c;通常是包含Java运行时环境的镜像&#xff0c;例如OpenJDK。可以从Docker Hub或其他镜像仓库中获取这些镜像。接下来&#xff0c;需要在…