当前位置: 首页 > news >正文

【C++11】可变模板参数

目录

可变模板的定义方式

参数包的展开方式

递归的方式展开参数包

STL中的emplace相关接口函数

STL容器中emplace相关插入接口函数

​编辑

模拟实现:emplace接口


C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改 进。

像之前学习的printf就是一个函数参数的可变参数,它可以接收多个任意类型,但它们只函数参数的可变参数,并不是模板的可变参数

printf的使用方法:

int printf( const char *format , ... );
本博客讲解的是函数模板的可变参数,不会涉及到类模板的可变参数

可变模板的定义方式

函数的可变参数模板定义方式如下:

template<class ...Args> //Args全称:arguments
返回类型 函数名(Args... args)
{//函数体
}

下面就是一个基本可变参数的函数模板

template <class ...Args>
void ShowList(Args... args)
{}

Args:是一个可变模板参数包

args:是一个函数形参参数包

说明一下:

模板参数Args前面有省略号,代表它是一个可变模板参数,我们将带省略号的参数称为 “参数包”,这个参数包中可以包含0到任意个模板参数,args则是一个函数形参参数包

现在我们可以向这个函数中传入多个不同的类型,并且可以通过sizeof算出参数包的参数个数

以下例代码为例:

template<class ...Args>
void ShowList(Args... args)
{cout << sizeof...(args) << endl;
}int main()
{ShowList(1);ShowList(1, 2);ShowList(1, 2, string("dict"));map<string, int> m1;ShowList(1, 2, 3, m1);return 0;
}

我们无法直接获取参数包args中的每个参数的, 只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

由于C++11语法不支持使用args[i]这样方式获取可变q参数,所以我们的用一些奇招来一一获取参数包的值。

错误示例:

template<class ...Args>
void ShowList(Args... args)
{//errorfor (int i = 0; i < sizeof...(args); ++i){cout << args[i] << endl;}
}

参数包的展开方式

递归的方式展开参数包

方式如下:

1.给函数模板新增一个参数,这样就可以从接收到的参数包分离出来一个参数

2.在函数模板中进行递归,不断的分离参数包中的参数

3.直到接收到最后一个参数结束

结束条件;

->1. 可以创建一个无参的函数来终止递归:当参数包中的参数为0时会调用该函数终止循环

void _ShowList()
{cout << endl;
}template<class T, class ...Args>
void _ShowList(T value, Args... args)
{cout << value << ' ';_ShowList(args...);
}int main()
{_ShowList(1, 2, string("dict"));return 0;
}

->2. 可以创建一个参数的函数来终止递归:当参数包中的参数为1时会调用该函数终止循环

template<class T>
void _ShowList(const T& t)
{cout << t << endl;
}template<class T, class ...Args>
void _ShowList(T value, Args... args)
{cout << value << ' ';_ShowList(args...);
}int main()
{_ShowList(1, 2, string("dict"));return 0;
}

但是使用该方法有一个弊端:我们在调用ShowList函数时必须至少传入一个参数,否则就会报错,因为此时无论是调用递归终止函数还是展开函数,都需要至少传入一个参数

使用sizeof...(args)算出参数个数的特性,利用它的特性做一个递归结束条件可以吗?不行!

template<class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value << ' ';if (sizeof...(args)){return;}ShowList(args...);
}

函数模板并不能调用,函数模板需要在编译时根据传入的实参类型进行推演,生成对应的函数,这个生成的函数才能够被调用。
而这个推演过程是在编译时进行的,当推演到参数包args中参数个数为0时,还需要将当前函数推演完毕,这时就会继续推演传入0个参数时的ShowList函数,此时就会产生报错,因为ShowList函数要求至少传入一个参数。
这里编写的if判断是在代码编译结束后,运行代码时才会所走的逻辑,也就是运行时逻辑,而函数模板的推演是一个编译时逻辑。

还有一种特殊的方式,该方法比较抽象,就是使用逗号表达式展开参数包

->3. 逗号表达式展开参数包

template<class T>
void CPPprint(const T& value)
{cout << value << ' ';
}template<class ...Args>
void ShowList(Args... args)
{int array[] = {( CPPprint(args), 0)...};cout << endl;
}

当我们在数组中不标注元素个数时,编译器会帮我们自动推导元素个数,这时它会帮我们展开参数包

如下:

int array[] = {( CPPprint(args), 0), CPPprint(args), 0),  CPPprint(args), 0),  CPPprint(args), 0)};

在调用CPPprint函数的同时,利用逗号运算符的特性进行对数组的初始化

其实也可以不使用逗号运算符完成该操作

template<class T>
int CPPprint(const T& value)
{cout << value << ' ';return 0;
}template<class ...Args>
void ShowList(Args... args)
{int array[] = { (CPPprint(args))... };cout << endl;
}

将被调用的函数设置一个返回值,调用之后返回0,这样就可以在编译器展开参数包调用函数时,通过返回值初始化

STL中的emplace相关接口函数

以便大家更好的理解emplace,先给大家看一段代码,可变模板参数的使用场景:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Data()~构造函数" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date()~拷贝构造" << endl;}private:int _year;int _month;int _day;
};template<class ...Args>
Date* Init(Args&&... args)
{Date* ret = new Date(args...);return ret;
}int main()
{Date* p1 = Init();Date* p2 = Init(2024);Date* p3 = Init(2024, 11);Date* p4 = Init(2024, 11, 12);Date d1(2, 3, 3);Date* p5 = Init(d1);return 0;
}

我们通过将参数传入参数包在编译期间通过将参数包展开的操作进行对象的构造

STL容器中emplace相关插入接口函数

C++11标准STL中的容器增加emplace版本的插入接口,比如list容器的push_front,push_back和insert函数,都增加了对应的emplace_front,emplace_back,emplace函数。如下:

emplace接口全部都是使用的可变参数模板

注意:两个&&是万能引用并不是右值引用

对比list中的push_back和emplace_back,对于emplace系列接口而言,它的主要优势就是直接在容器内部构造元素可以结合我上面给的场景进行理解,而不是构造一个临时对象在复制或移动到容器中可以有效的避免拷贝和移动操作

以emplace和push_back为例:

调用push_back函数插入元素时,可以传入左值对象或者右值对象,也可以使用列表初始化

调用emplace时可以传左值对象或者右值对象,但是不能使用列表初始化,emplace系列最大的特点就是,插入元素时可以传入用于构造元素的参数包

比如:

int main()
{list<pair<nxbw::string, int>> mylist;pair<nxbw::string, int> kv("nxbw", 10);mylist.emplace_back(kv); //传左值mylist.emplace_back(make_pair("nxbw", 10)); //传右值mylist.emplace_back("nxbw", 10); //传参数包mylist.push_back(kv); //传左值mylist.push_back(make_pair("nxbw", 10)); //传右值mylist.push_back({ "nxbw", 10 }); //使用列表初始化return 0;
}

原地构造:使用emplace,你可以提供构造元素所需的参数,容器会直接在emplace接口的实现中构造该对象

emplace系列接口的工作流程

emplace系列接口的工作流程如下:

  1. 先通过空间配置器为新结点获取一块内存空间,注意这里只会开辟空间,不会自动调用构造函数对这块空间进行初始化。
  2. 然后调用allocator_traits::construct函数对这块空间进行初始化,调用该函数时会传入这块空间的地址和用户传入的参数(需要经过完美转发)。
  3. 在allocator_traits::construct函数中会使用定位new表达式,显示调用构造函数对这块空间进行初始化,调用构造函数时会传入用户传入的参数(需要经过完美转发)。
  4. 将初始化好的新结点插入到对应的数据结构当中,比如list容器就是将新结点插入到底层的双链表中。

emplace系列接口的意义

由于emplace系列接口的可变模板参数的类型都是万能引用,因此既可以接收左值对象,也可以接收右值对象,还可以接收参数包。

  1. 如果调用emplace系列接口时传入的是左值对象,那么首先需要先在此之前调用构造函数实例化出一个左值对象,最终在使用定位new表达式调用构造函数对空间进行初始化时,会匹配到拷贝构造函数
  2. 如果调用emplace系列接口时传入的是右值对象,那么就需要在此之前调用构造函数实例化出一个右值对象,最终在使用定位new表达式调用构造函数对空间进行初始化时,就会匹配到移动构造函数
  3. 如果调用emplace系列接口时传入的是参数包,那就可以直接调用函数进行插入,并且最终在使用定位new表达式调用构造函数对空间进行初始化时,匹配到的是构造函数。

总结一下:

  • 传入左值对象,需要调用构造函数+拷贝构造函数
  • 传入右值对象,需要调用构造函数+移动构造函数
  • 传入参数包,只需要调用构造函数

当然,这里的前提是容器中存储的元素所对应的类,是一个需要深拷贝的类,并且该类实现了移动构造函数。否则在调用emplace系列接口时,传入左值对象和传入右值对象的效果都是一样的,都需要调用一次构造函数和一次拷贝构造函数。

实际emplace系列接口的一部分功能和原有各个容器插入接口是重叠的,因为容器原有的push_back、push_front和insert函数也提供了右值引用版本的接口,如果调用这些接口时如果传入的是右值对象,那么最终也是会调用对应的移动构造函数进行资源的移动的。

emplace接口的意义:

emplace系列接口最大的特点就是支持传入参数包,用这些参数包直接构造出对象,这样就能减少一次拷贝,这就是为什么有人说emplace系列接口更高效的原因。
但emplace系列接口并不是在所有场景下都比原有的插入接口高效,如果传入的是左值对象或右值对象,那么emplace系列接口的效率其实和原有的插入接口的效率是一样的。
emplace系列接口真正高效的情况是传入参数包的时候,直接通过参数包构造出对象,避免了中途的一次拷贝。
 

通过下面的场景我们来验证一下:

namespace nxbw
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移动语义" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动语义" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}
int main()
{list<pair<nxbw::string, int>> mylist;pair<nxbw::string, int> kv("nxbw", 10); //构造mylist.emplace_back(kv); //传左值,mylist.emplace_back(pair<nxbw::string, int>("nxbw", 10)); //传右值mylist.emplace_back("nxbw", 10); //传参数包return 0;
}

由于我们在string的构造函数、拷贝构造函数和移动构造函数当中均打印了一条提示语句,因此我们可以通过控制台输出来判断这些函数是否被调用。

下面我们用一个容器来存储模拟实现的string,并以不同的传参形式调用emplace系列函数。比如:

说明一下:

模拟实现string的拷贝构造函数时复用了构造函数,因此在调用string拷贝构造的后面会紧跟着调用一次构造函数。
为了更好的体现出参数包的概念,因此这里list容器中存储的元素类型是pair,我们是通过观察string对象的处理过程来判断pair的处理过程的。
这里也可以以不同的传参方式调用push_back函数,顺便验证一下容器原有的插入函数的执行逻辑。比如:

int main()
{list<pair<nxbw::string, int>> mylist;pair<nxbw::string, int> kv("nxbw", 10);mylist.push_back(kv); //传左值mylist.push_back(pair<nxbw::string, int>("nxbw", 10)); //传右值mylist.push_back({ "nxbw", 10 }); //使用列表初始化return 0;
}

模拟实现:emplace接口

namespace nxbw
{// 模拟实现list在之前的章节有提过,这里只是将原来的代码多增加一些接口的片段代码// 这是list需要用到的节点类template<class T>struct __list_node{__list_node(const T& val = T()):_data(val), _prev(nullptr), _next(nullptr){}// 这里需要在原来的基础上需要增加一个可变模板参数模板的构造函数,方便下面使用newtemplate<class ...Args>__list_node(Args&& ...args): _data(std::forward<Args>(args)...), _prev(nullptr), _next(nullptr){}T _data;__list_node* _prev;__list_node* _next;};template<class T>struct list{template<class ...Args>iterator emplace(iterator position, Args&&... args){node* cur = position._node;node* prev = cur->_prev;// 函数参数包的完美转发node* newnode = new node(forward<Args>(args)...);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(cur);}template<class ...Args>void emplace_back(Args&&... args){// 函数参数包的完美转发emplace(end(), forward<Args>(args)...);}// 获取节点函数,这里更新成了万能引用版的template<class T>node* get_node(T&& val = T()){node* new_node = new node(forward<T>(val)); // 完美转发new_node->_prev = new_node;new_node->_next = new_node;return new_node;}private:__list_node<T>* _head; // 指向节点类的指针};
};

emplace系列和push_back以及insert的区别

效率方面:对于左值引用版本的push_back和insert来说确实有很大的效率提升,对于右值引用版本的push_back和insert来说效率其实差不多,因为移动赋值/拷贝代价足够小

构造复杂对象:当元素的构造比叫复杂时,emplace可以让代码更简洁,直接传入构造参数即可

相关文章:

【C++11】可变模板参数

目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现&#xff1a;emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板&#xff0c;相比 C9…...

AAAI-2024 | 大语言模型赋能导航决策!NavGPT:基于大模型显式推理的视觉语言导航

作者&#xff1a;Gengze Zhou, Yicong Hong, Qi Wu 单位&#xff1a;阿德莱德大学&#xff0c;澳大利亚国立大学 论文链接&#xff1a; NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models &#xff08;https://ojs.aaai.org/index.p…...

@HeadFontStyle注解属性介绍

HeadFontStyle 是一个自定义的 Java 注解&#xff0c;它用于指定 Excel 单元格字体的样式属性。这个注解可以应用于方法中&#xff0c;用来动态地设置 Excel 文件中单元格的字体样式。下面是 HeadFontStyle 注解中各个属性的详细介绍&#xff1a; 1. fontName (String) 类型:…...

Exchange ProxyLogon 攻击链利用详解

目录 ProxyLogon 攻击链 影响版本 CVE-2021-26855 SSRF 复现 验证是否存在漏洞 详细漏洞利用 CVE-2021–27065 任意文件写入复现 ProxyLogon 一键利用 CVE-2021-26855 与 CVE-2021-27065 是微软在2021年3月2日发布的高危漏洞公告。这套组合拳被称为ProxyLogon,可直接获…...

C++小碗菜之五:关键字static

“一个人的命运啊&#xff0c;当然要靠自我奋斗&#xff0c;但也要考虑到历史的行程。” ——2009年4月23日在视察中国联合工程公司时的讲话 目录 ​编辑 前言 static在局部作用域中的作用 给出例子&#xff1a; 修改上面给出的例子&#xff1a; 为什么不使用全局变量…...

deepstream笔记

创建pipeline pipeline gst_pipeline_new("audio-player");创建filesrc类型元素并命名为file-source&#xff1b; GstElement *source gst_element_factory_make("filesrc", "file-source");通过元素名file-source获取元素对应的指针&#x…...

Pinpoint 是一个开源的分布式追踪系统

pinpointagent2.2.2.tar 是 Pinpoint 的一个版本&#xff0c;Pinpoint 是一个开源的分布式追踪系统&#xff0c;专门用于对 Java 应用程序进行性能监控、日志记录和故障诊断。它可以帮助开发人员和运维人员追踪和分析微服务架构中服务之间的调用链&#xff0c;并进行性能分析。…...

H3C交换机远程登录基本配置

设备信息 H3C Comware Software, Version 7.1.070, Release 6312P02 Copyright (c) 2004-2021 New H3C Technologies Co., Ltd. All rights reserved. H3C S6520X-54QC-EI Telnet登录设备基本配置 1、开启telnet服务 system-view telnet server enable 2、telnet登录设备终…...

python关闭线程池来关闭线程

在 Python 中&#xff0c;使用线程池&#xff08;如 concurrent.futures.ThreadPoolExecutor 或 multiprocessing.pool.ThreadPool&#xff09;来管理和执行多个线程是一种常见的并发编程方式。关于关闭线程池以及关闭后线程的状态&#xff0c;以下是详细的解释和指导。 使用 …...

生成式AI:药学科普的新引擎

在信息爆炸的时代&#xff0c;药学知识的普及显得尤为重要。而今&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;正以其强大的内容生成和数据分析能力&#xff0c;悄然改变着传统的药学科普模式。它不仅能加速信息的传递&#xff0c;更能为患者提供个性化、易…...

洛谷 p3392 涂条纹

题目&#xff1a; 思路&#xff1a; 简单的模拟题&#xff0c;模拟题好麻烦&#xff0c;但是思路走好就可以。首先我们可以求出每一行&#xff0c;红&#xff0c;蓝&#xff0c;白的个数。涂蓝色和白色为了涂色更少&#xff0c;所以涂蓝色要选择第i行蓝色个数最多的&#xff0…...

64.基于SpringBoot + Vue实现的前后端分离-新闻资讯系统(项目 + 论文)

项目介绍 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;文章信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…...

Y3编辑器教程8:资源管理器与存档、防作弊设置

文章目录 一、资源管理器简介1.1 界面介绍1.2 资源商店1.3 AI专区1.3.1 AI文生图1.3.2 AI图生图1.3.3 立绘头像 二、导入导出2.1 文件格式2.2 模型导入2.2.1 模型制作后导出2.2.2 模型文件导入Y3编辑器2.2.3 Y3编辑器角色、装饰物模型要求 2.3 纹理导入2.4 材质贴图2.4.1 材质支…...

智慧社区电子商务系统:实现社区资源的数字化管理

2.1vue技术 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项…...

精准提升:从94.5%到99.4%——目标检测调优全纪录

&#x1f680; 目标检测模型调优过程记录 在进行目标检测模型的训练过程中&#xff0c;我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化&#xff0c;每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…...

【LLM论文日更】| 训练大型语言模型在连续潜在空间中进行推理

论文&#xff1a;https://arxiv.org/pdf/2412.06769代码&#xff1a;暂未开源机构 &#xff1a;Meta领域&#xff1a;思维链发表&#xff1a;arxiv 研究背景 研究问题&#xff1a;这篇文章要解决的问题是如何在大语言模型&#xff08;LLMs&#xff09;中实现一种新的推理范式&…...

智能家居实训室中,STC单片机驱动的“互联网+”智能家居系统设计

一、引言 随着经济的快速发展&#xff0c;人们对家居环境的智能化、网络化需求日益增强&#xff0c;智能家居的研究也因此受到了国内外相关机构的广泛关注。STC单片机凭借其卓越的性能和广泛的应用领域&#xff0c;成为了智能家居系统设计的优选方案。作为一种先进的微控制器&…...

《C++ 赋能强化学习:Q - learning 算法的实现之路》

在当今科技飞速发展的时代&#xff0c;人工智能无疑是最热门的领域之一&#xff0c;而强化学习作为其中的重要分支&#xff0c;正逐渐改变着我们解决复杂问题的方式。Q - learning 算法作为强化学习中的经典算法&#xff0c;在众多领域如游戏、机器人控制、资源管理等有着广泛的…...

三维模型中的UV展开是什么意思?它有什么优势?

UV展开涉及将三维模型的表面展开为一个或多个二维区域&#xff0c;以便将纹理图像正确地映射到模型上。这个过程类似于将一个立体物体的表面切割并平铺开来。UV坐标是用于在二维纹理图像中定位颜色和细节的坐标系统&#xff0c;U和V分别代表纹理图像的水平和垂直轴。 UV展开它…...

怎么在ubuntu系统上安装qt项目的打包工具linuxdeployqt

引言 安装linuxdeployqt方案一方案二 在ubuntu系统上开发的项目最后需要完成打包&#xff0c;qtcreator本身就用一个打包工具&#xff0c;在ubuntu系统上是linuxdeployqt。本文主要记录一下怎么在ubuntu系统上安装qt打包工具linuxdeployqt。 安装linuxdeployqt 前提是已经安装…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...