Perfect Forwarding(完美转发)
文章目录
- 1. 引用折叠
- 2. 万能引用
- 3. 完美转发
- 3.1对比:std::move and std::forward
- 比较
- 3.2使用时机
- 3.3 返回值优化(RVO)
- 两个前提条件
- 注意事项
- 4. 完美转发失败情况
- 完美转发失败
- 五种情况
完美转发的实现要依赖于模版类型推导和引用折叠和万能引用。
1. 引用折叠
- 在C++中,“引用的引用"是非法的。像
auto& & rx = x;
(两个引用之间有空格),这种直接定义引用的引用是不合法的,但是在编译器通过类型别名或模版类型推导等语境中,可能间接定义出"引用 的引用”,这是引用会形成"折叠" - 引用折叠会发生的场景:模版的实例化,auto类型推导,创建和运用typedef 和别名声明以及decltype语境
引用折叠规则:
所有的引用折叠最终都代表一个引用,要么是左值引用,要么是右值引用。规则就是:
如果任一引用为左值引用,则结果为左值引用。否则(即两个都是右值引用),结果为右值引用。
-----根本原因是:在一处的声明为左值,就说明该对象为持久对象,编译器就必须此对象可靠(左值)《Effective Modern C++》
x&& && 折叠为 x&&
x& &,x&& &, x& && 都折叠为 x&
编程实验:
class widget
{};template<typename T>
void func(T&& param)
{}widget widgetFactory()
{return widget();
}template<typename T>
class Foo
{
public:
typedef T&& RvalueRefToT;
};int main()
{int x = 0;int& rx = x;//折叠引用发生的场景1.模版模版实例化widget w1;func(w1); //w1为左值,T被推导为widget& , 代入得void func(Widget& && param);//引用折叠后得:void func(Widget& param)func(widgetFactory()); //传入右值,T被推导为widget,代入的得void func(Widget&& param)//这里没有发生引用折叠//折叠引用发生的场景2.---auto类型推导auto&& w2 = w1; //w1为左值auto被推导为widget&,代入得:widget& &&, 引用折叠后:widget& w2;auto&& w3 = widgetFactory(); //函数返回widget,为右值,auto被推导为widget,代入的: widget w3;//引用折叠发生的语境3——tyedef和usingFoo<int&> f; //T被推导为int& ,代入得:typedef int& && RvalueRefToT,引用折叠后:typedef int& RvalueRefToT//引用折叠发生的语境4——decltypedecltype(x) && var1 = 10; //由于x是int类型: 代入得 int&& var1;decltype(rx) && var2 = x; //由于rx为int& 类型,代入得 int& var2;return 0;
}
2. 万能引用
万能引用:既可以接受左值类型的参数又可以接受右值类型的参数。
T&&含义:
-
当T是一个具体的类型时,T&&表示右值引用,只能绑定到右值。
-
当涉及T类型推导时,T&&为万能引用。若用右值初始化万能引用,则T&&为右值引用。若用左值初始化万能引用,则T&&为左值引
条件:T&&是万能引用的两个条件:
(1)必须涉及类型推导;
(2)声明的形式也必须正好形如“T&&”。并且该形式被限定死了,任何对其修饰都将剥夺T&&成为万能引用的资格
template<typename T>
ReturnType Function(T&& parem)
{// 函数功能实现
}
编程实验:
#include <iostream>
using namespace std;void fun(int& x) { cout << "call lvalue ref" << endl; }
void fun(int&& x) { cout << "call rvalue ref" << endl; }
void fun(const int& x) { cout << "call const lvalue ref" << endl; }
void fun(const int&& x) { cout << "call const rvalue ref" << endl; }template<typename T>
void PerfectForward(T&& t)
{std::cout << "T is a ref type?: " << std::is_reference<T>::value << std::endl;std::cout << "T is a lvalue ref type?: " << std::is_lvalue_reference<T>::value << std::endl;std::cout << "T is a rvalue ref type?: " << std::is_rvalue_reference<T>::value << std::endl;fun(forward<T>(t));
}int main()
{PerfectForward(10); // call rvalue refint a = 5;PerfectForward(a); // call lvalue refPerfectForward(move(a)); // call rvalue refconst int b = 8;PerfectForward(b); // call const lvalue refPerfectForward(move(b)); // call const rvalue refsystem("pause");return 0;
}/*输出结果
T is a ref type?: 0
T is a lvalue ref type?: 0
T is a rvalue ref type?: 0 //万能引用绑定右值,T会被推导为 T 类型
call rvalue refT is a ref type?: 1
T is a lvalue ref type?: 1
T is a rvalue ref type?: 0 //万能引用绑定左值,T会被推导为T& 类型
call lvalue refT is a ref type?: 0
T is a lvalue ref type?: 0
T is a rvalue ref type?: 0
call rvalue refT is a ref type?: 1
T is a lvalue ref type?: 1
T is a rvalue ref type?: 0
call const lvalue refT is a ref type?: 0
T is a lvalue ref type?: 0
T is a rvalue ref type?: 0
call const rvalue ref
*/
利用引用折叠进行万能引用初始化类型推导
- 当万能引用(T&& param )绑定到左值时,由于万能引用也是引用,而左值只能绑定到左值引用,因此,T会被推导为T& 类型,从而 param 的类型为 T& && ,引用折叠后类型变为: T&;
- 当万能引用(T&& param )绑定到左值时,右值只能绑定到右值引用,因此,T会被推导为 T 类型,从而 param 的类型为 T&&(右值引用)。
总结:万能引用就是利用模板推导和引用折叠的相关规则,生成不同的实例化模板来接收传进来的参数。
3. 完美转发
指的是 函数模板可以将自己的参数 “完美” 地转发给内部调用的其它函数 ,不仅能准确地转发参数的值,还能 保证被转发参数的左、右值属性不变 。
//左值版本
template<class T>
constexpr T&& forward(std::remove_reference_t<T>& arg) noexcept
{// forward an lvalue as either an lvalue or an rvaluereturn (static_cast<T&&>(arg));//可能发生引用折叠
}
//右值版本
template<class T>
constexpr T&& forward(std::remove_reference_t<T>&& arg) noexcept
{// forward an rvalue as an rvaluereturn (static_cast<T&&>(arg));
}
为什么引入完美转发?
编程实验:
void print(const int& t) //左值版本
{cout << "void print(const int& t)" << endl;
}
void print(int&& t) //右值版本
{cout << "void print(int&& t)" << endl;
}template<typename T>
void testForward(T&& parm)
{//不完美转发print(parm); //parm 是形参,是左值。调用void print(const int& t),永远调用左值版本 print(std::move(parm)); //parm是 右值。调用void print(int&& t),永远调用右值版本//完美转发print(std::forward<T>(parm));//只有这里才会根据传入parm的实参类型的左右值进行转发
}
int main()
{cout << "测试右值:" << endl;testForward(1);cout << "测试左值:" << endl;int x = 1;testForward(x);return 0;
}/*输出结果:
测试右值
void print(const int& t)
void print(int&& t)
void print(int&& t) //完美转发,这里转入的值为1,为右值,调用右值版本的print
测试左值
void print(const int& t)
void print(int&& t)
void print(const int& t)//完美转发,这里转入的值为x,为左值,调用左值版本的print
*/
分析std:forward 实现条件转发的原理:以widget类对象为例
:::info
总结:
- 当传递给func函数的实参类型是左值widget时,T被推导为widget&类型,然后forward会实例化为std:forward<widget&>,并返回widget&(左值引用)。
- 当传递给func函数的实参类型是右值widget时,T被推导为widget类型,然后forward会实例化为std:forward,并返回widget&&(右值引用,注意:匿名的右值引用是右值)。
- std:forward会根据传递给func函数的实参的左/右值类型进行转发,当传递给func函数左值实参时,forward返回左值引用,并将改值转发给process函数,而当传递给func函数右值实参时,forward返回右值引用,并将右值转发给process函数。
:::
3.1对比:std::move and std::forward
比较
- move和forward都是仅仅执行强制类型转换的函数,std:move 无条件的将实参强制转换为右值。而std:forward 则仅是在某个特定的条件满足时(转入func函数的实参时右值)才进行强制类型转换。
- std:move 并不进行任何移动,std:forward 也不进行任何转发。这两者都在运行期间无所作为,他们都不会再生成任何可执行代码。连一个字节都不会生成。
3.2使用时机
- 针对右值引用的最后一次使用实施std:move,针对万能引用的最后一次使用实施std:forward。
- 在按值返回的函数中,如果返回的是一个绑定在右值引用或万能引用的对象时,可以实施std:move 或 std:forward。因为如果原始对象是右值,它的值就应被移动到返回值上,而如果是左值,就必须通过复制构造出副本作为返回值。
3.3 返回值优化(RVO)
两个前提条件
- 局部对象类型和函数返回值类型相同。
- 返回的就是局部对象本身。(包含局部对象和作为return语句中的临时对象等)。
注意事项
- 在RVO的前提条件满足时,要么避免复制,要么会自动调用std::move隐式实施于返回值。
- 按值传递的函数形,把他们作为函数的返回值时,情况与返回值优化类似,编译器会自动选择第二种处理方案,即返回时将形参转为右值处理。
- 如果局部变量有资格进行RVO优化,就不要将std:move 或 std:forward用在这些变量上,因为这可能会让返回值丧失优化的机会。
//针对右值引用实施std:move 针对万能引用实施std:forward
class Data {};
class widget
{std::string _name;std::shared_ptr<Data> _ptr;
public:widget(){cout << "widget()" << endl;}//复制构造函数widget(const widget& w):_name(w._name), _ptr(w._ptr){cout << "widget(const widget& w)" << endl;}//针对右值引用使用std:movewidget(widget&& rhs):_name(std::move(rhs._name)), _ptr(std::move(rhs._ptr)){cout << "widget(widget&& w)" << endl;}//针对万能引用使用std:forward.//注意这里使用万能引用来代替两个重载版本:void setName(const string& )和 void setName(string&&)//好处是当使用字符串字面量时,万能引用的版本的效率更高,如:w.setName("santa"),此时字符串会被推导为//const char (&)类型,然后直接转给setName函数,(可以避免先通过字面量来构造string对象), //并将该类型直接转给name的构造函数,节省了一个构造和释放临时对象的开销,效率更高template<class T>void setNaem(T&& newname){if (newname != _name)//第一次使用newname{_name = std::forward<T>(newname);//针对万能引用的最后一次实施forward}}
};//2. 按值返回函数//2.1 按值返回的是一个绑定到右值引用的对象class complex {double _x;double _y;public:complex(double x = 0, double y = 0):_x(x),_y(y){}complex& operator+=(const complex& rhs){_x += rhs._x;_y += rhs._y;return *this;}};complex operator+(complex&& lhs, complex& rhs){lhs += rhs;return std::move(lhs);}//2.2 按值返回绑定到一个万能引用对象template<typename T> auto test(T&& t){return std::forward<T>(t);//由于t是一个万能引用对象。按值返回时实施std:forward//如果对象是一个右值,则被移动到返回值上,如果对象是左值//则会被拷贝到返回值。}//3. RVO优化//3.1 返回局部对对象widget makewidget(){widget w;return w; //返回局部对象,瞒足RVO优化的两个条件。为避免复制,会直接在返回值内存上创建w对象。//如果改为:std:move(w);由于返回值类型不同,(widget& 返回值类型是 widget)//会剥夺RVO优化的机会,就会先创建w对象,在移动给返回值,无形中增加了移动动作。//对于这种满足RVO优化条件的,在某些条件下无法避免复制(如:多路返回),编译器仍会//将w转为右值,即 return std:move(w) ,而无需显示的std:move!!}//3.2按值形参作为返回值widget makewidget(widget w)//注意这里的w是按值传递的{//...return w;//这里虽然不满足RVO条件,(w是形参,不是函数的局部对象),当时仍会被编译器优化,默认的转为右值}
int main()
{cout << "1. 针对右值引用实施std::move,针对万能引用实施std::forward" << endl;widget w;w.setNaem("SantaClaus");cout << "2. 按值返回时" << endl;auto t1 = test(w);auto t2 = test(std::move(w));cout << "3. RVO优化" << endl;widget w1 = makewidget(); //按值返回局部对象widget w2 = makewidget(w1); //按值返回形参对象return 0;
}
//运行结果:
/*
1. 针对右值引用实施std::move,针对万能引用实施std::forward
widget()
2. 按值返回时
widget(const widget& w)
widget(widget&& w)
3. RVO优化
widget()
widget(widget&& w)widget(const widget& w)
widget(widget&& w)
*/
4. 完美转发失败情况
完美转发失败
- 完美转发不仅转发对象,还转发其类型,左右值特征以及是否带有const 或 volation 等修饰词。而完美转发的失败,主要来源于模版类型推导失败或结果是错误的类型。
- 实例说明:假设转发的目标函数f,而转发函数fwd(天然就应该是泛型)
template<typename... Ts>
void fwd(Ts&&... param)
{f(std::forward<Ts>(param)...);
}f(experssion); //如果本语句执行了某操作
fwd(experssion);//而用同一实参调用fwd 则会执行不同操作,则完美转发失败
五种情况
- 使用{}初始化列表时
分析:由于转发函数是模版函数,而在模版类型推导中,大括号不能自动推导为std:initializer_list
解决方案:用auto 声明一个局部变量,在将局部变量传递给转发函数。
- 0和NULL作空指针时
分析:0或NULL以空指针之名传递给模版时,类型推导的结果是整型,而不是所希望的指针类型。
解决方案:传递nullptr,而非0或NULL。
- 仅声明static成员变量而无定义时
分析:C++中常量一般是进入符号表中的只有对其取地址是才会实际分配地址,调用f函数时,其实参是直接从符号表中取值,此时不会出现问题,但当调用fwd由于其形参是万能引用,而引用本质上是可解引用的指针。因此当传入fwd会要求准备某块内存以供解引用出该变量出来,但因其未定义,也就没有实际的内存空间,编译时可能失败。注意:如果是static const 声明的常量的值是整形,或者布尔型的,那么就可以直接在类中进行定义初始化,如果是浮点数和自定义类型,那么就需要再类外进行定义初始化,
解决方案:在类外定义该成员变量。注意着这变量在生声明时,一般会先给初始值,因此在定义时,无需也不能再重复指定初始值
- 使用重载函数名或模版函数名时
分析:由于fwd是模版类型参数,其形参没有任何类型的信息。当传入重载函数名或模版函数时(代表许多函数),就会导致fwd的形参不知绑定到哪一个函数上。
解决方案:在调用fwd函数时手动为形参指定类型信息。
- 转发位域时
分析:位域是由机器字的若干任意部分组成,(任意int的3 到 5个比特位),但这样的实体是无法取地址的,而fwd的形参是引用,本质就是指针,所以也没有办法创建指向任意比特的指针。
解决方法:制作位域的副本,并以该副本来调用转发函数。
//大括号初始化列表
void f(const std::vector<int>& v)
{cout << "void f(const std::vector<int>& v)" << endl;
}
//2. 用0或NULL作空指针时
void f(int x)
{cout << "void f(intx)" << endl;
}
//3.仅声明static const的整型变量而无定义
class widget
{
public:static const double MinValue ;//仅声明无定义(静态变量需要再类外定义)
};
const double widget::MinValue = 28.10;//在类外定义无需也不能重复定义初始值
//4. 使用重载函数名或模版函数名
int f(int (*pf)(int))
{cout << "int f(int (*pf)(int))" << endl;return 0;
}
int processVal(int value)
{return 0;
}
int processVal(int value,int priority)
{return 0;
}//5. 位域
struct IPv4Header
{
std::uint32_t version : 4,
IHL : 4,DSCP : 6,ECN : 2,totalLength : 16;
//...
};
template<typename T>
T workonval(T param)//模版函数代表许多函数
{return param;
}//用于测试的转发函数
template<typename ...Ts>
void fwd(Ts&& ...param)
{f(std::forward<Ts>(param)...);//目标函数
}
int main()
{cout << "-------------------1. 大括号初始化列表---------------------" << endl;//1.1 用同一实参分别调用f和fwd函数f({ 1,2,3 });//{1,2,3}会被隐式转函为vector<int>//fwd({ 1,2,3 });//编译失败,由于fwd是模版函数,而模版推导时{}不能被自动推导为std::initializar_list<int>//解决方案:auto il = { 1,2,3 };fwd(il);cout << "-------------------2. 0或NULL用作空指针-------------------" << endl;//2.1 用同一实参分别调用f和fwd函数f(NULL); //调用void f(int) 函数fwd(NULL); //NULL被推导为int,仍调用void f(int )//2.2 解决方案:使用nullptrf(nullptr); //匹配int f(int (*pf)(int ))fwd(nullptr);cout << "-------3. 仅声明static const的整型成员变量而无定义--------" << endl;//3.1 使用同一实参分别调用f和fwd函数f(widget::MinValue); //调用用void f(int) 函数实参从符号表中取得,编译成功fwd(widget::MinValue); //fwd的形参是引用,而引用的本质是指针,但fwd使用到该实参时需要解引用//这里会因为没有为MinValue分配内存·而出现编译错误//3.2解决方案:在类外定义该变量cout << "-------------4. 使用重载函数名或模板函数名---------------" << endl;//4.1 使用同一实参分别调用f和fwd函数f(processVal); //由于形参是int(*pf)(int),带有类型信息会匹配int processVal(int value)//fwd(processVal);//error,fwd的形参不带任何类型信息,不知到会匹配到哪个processVal函数//4.2 解决方案using processFuncType = int(*)(int);processFuncType processValOPtr = processVal;fwd(processValOPtr);fwd(static_cast<processFuncType>(workonval));//调用int f((*pf)(int))cout << "----------------------5. 转发位域时---------------------" << endl;//5.1 用同一实参分别调用f和fwd函数IPv4Header ip = {};f(ip.totalLength); //调用void f(int )//fwd(ip.totalLength); //error,fwd形参是引用,由于位域是比特位组成的,无法比特位的引用//解决方案:创建位域的副本,并传给fwdauto length = static_cast<std::uint16_t>(ip.totalLength);fwd(length);return 0;}
/*输出结果:
-------------------1. 大括号初始化列表---------------------
void f(const std::vector<int>& v)
void f(const std::vector<int>& v)
-------------------2. 0或NULL用作空指针-------------------
void f(intx)
void f(intx)
int f(int (*pf)(int))
int f(int (*pf)(int))
-------3. 仅声明static const的整型成员变量而无定义--------
void f(intx)
void f(intx)
-------------4. 使用重载函数名或模板函数名---------------
int f(int (*pf)(int))
int f(int (*pf)(int))
int f(int (*pf)(int))
----------------------5. 转发位域时---------------------
void f(intx)
void f(intx)
*/
参考博客:链接: link
链接: link
链接: link
(全文完)
相关文章:

Perfect Forwarding(完美转发)
文章目录 1. 引用折叠2. 万能引用3. 完美转发3.1对比:std::move and std::forward比较 3.2使用时机3.3 返回值优化(RVO)两个前提条件注意事项 4. 完美转发失败情况完美转发失败五种情况 完美转发的实现要依赖于模版类型推导和引用折叠和万能引用。 1. 引…...

PHP露营地管理平台小程序系统源码
⛺️【露营新风尚】露营地管理平台系统全攻略⛺️ 🏕️一、露营热潮下的管理难题:如何高效运营露营地?🤔 随着露营文化的兴起,越来越多的人选择在大自然中享受宁静与自由。然而,露营地的管理却面临着诸多…...
速盾:vue的cdn是干嘛的?
CDN,即内容分发网络(Content Delivery Network),是一种将网站的静态资源分发到全球各个节点并缓存起来的技术。它可以帮助网站提供更快的加载速度,更好的用户体验,并且可以减轻源服务器的负载压力。 Vue.j…...

线性代数:Matrix2x2和Matrix3x3
今天整理自己的框架代码,将Matrix2x2和Matrix3x3给扩展了一下,发现网上unity数学计算相关挺少的,所以记录一下。 首先扩展Matrix2x2: using System.Collections; using System.Collections.Generic; using Unity.Mathemati…...
Windows 中 Electron 项目实现运行时权限提升以杀掉特定进程
#Windows 中 Electron 项目实现运行时权限提升以杀掉特定进程 一、引言 在 Windows 操作系统中,有时我们需要以管理员权限来执行某些操作,特别是当需要杀掉由管理员启动的进程时。Electron 是一个开源的框架,用于使用 JavaScript、HTML 和 C…...

赠你一只金色的眼 - 富集分析和表达数据可视化
GOplot包介绍 GOplot包用于生物数据的可视化。更确切地说,该包将表达数据与功能分析的结果整合并进行可视化。但是要注意该包不能用于执行这些分析,只能把分析结果进行可视化。在所有科学领域,由于空间限制和结果所需的简洁性,切…...

鸿蒙的进击之路
1. 题记: 为什么要写鸿蒙,因为她是华为的,为什么是华为就要写,因为华为背负了国人太多太多的包袱,或点赞或抨击。 我是强烈支持华为的,但我会客观公正地去评价华为的产品,就比如这篇博文&#…...
c语言中的线程管理pthread详解
在C语言中,多线程编程常用的POSIX线程(POSIX Threads, pthreads)库主要由pthread.h头文件提供。pthread.h定义了许多用于线程创建、管理、同步的函数和数据结构。下面是pthread.h中的核心概念和主要函数的详细介绍。 1. 基本概念 线程:线程是一个轻量级的进程,可以并发执…...
关于qiskit版本>1.0.0,execute函数被替换
关于下列代码,当qiskit版本大于1时,无法使用execute函数,定义transpiled_circuit和run函数来替换。 import numpy as np from qiskit import QuantumCircuit, Aer, executedef calculate_hydrogen_energy():# Definir el circuito cunticoci…...
给 Docker 配置网络代理
参考 https://www.cnblogs.com/Chary/p/18096678如何优雅的给 Docker 配置网络代理 有时因为网络原因,比如公司 NAT,或其它啥的,需要使用代理。Docker 的代理配置,略显复杂,因为有三种场景。但基本原理都是一致的,都是利用 Linux 的 http_proxy 等环境变量。 Dockerd 代…...
软件测试基础十七(python Unittest)
Unittest 一、Unittest 简介 unittest是 Python 内置的标准测试框架,用于编写和运行单元测试。它提供了一组工具和类,帮助开发者组织、编写和执行测试用例,以验证代码的正确性。 二、Unittest 核心要素 1. TestCase(测试用例类…...

技术领导者的道与术:从领导者到领导力
目录标题 领导者现实看起来是这样技术领导者不应该和个人坐在一起技术领导力仍然是必须的从技术领导到技术领导力小结领导者 你可能想成为或者已经是一位技术领导者,估计你现在心里想成为超级英雄的想法正在爆棚。 你是Java、JavaScript、Angular等技术的专家,公司的项目代…...
Starrocks Compaction的分析
背景 本文基于 Starrocks 3.1.7 结论 Starrocks 会启动一个线程周期性的去进行Compaction,该周期间隔为 200 MS, 该Compaction以table的partition为切入点,tablet(也就是bucket)为粒度进行task的创建。 分析 CompactionMgr start 方法会启动一个Com…...
淘淘商城实战高并发分布式项目(有源码)
通过百度网盘分享的文件:淘淘商城实战高并发分布式项目(有源码) 链接:https://pan.baidu.com/s/1V94gRALxHgMVwpcXoE-miA?pwdglu7 提取码:glu7 在互联网技术飞速发展的当下,高并发分布式项目成为了众多电商平台等大型应用的核心…...

内网部署web项目,外网访问不了?只有局域网能访问!怎样解决?
相关技术 要实现“内网部署,外网访问”,可以使用内网穿透、VPN技术、DMZ主机、端口映射等方法。以下是对这些方法的详细解释: 一、内网穿透 内网穿透是一种技术,它通过将内网设备映射到公网上的方式,实现外网访问内…...
Jenkins系列
jenkins 1、搭建Jenkins 搭建Jenkins 2、这是什么 3、这是什么 4、 这是什么 5、这是什么 文章目录 jenkins1、搭建Jenkins2、这是什么3、这是什么4、 这是什么5、这是什么 前言 前言 提示:这里可以添加本文要记录的大概内容: 例如:随…...
技术总结(二十四)
一、Redis 分布式锁的常见使用场景有哪些? 资源竞争控制 数据库事务控制:在分布式系统中,多个服务可能会同时对数据库中的同一行数据进行操作。例如,在一个电商系统里,多个订单处理服务可能会同时尝试更新同一个订单的…...

原生鸿蒙应用市场:赋能开发者全生命周期服务体验
文章目录 背景自动化检测前移:早发现,早解决技术细节:静态代码分析与兼容性测试应用场景 按需加载:优化性能,提升用户体验技术细节:模块化与懒加载实现应用场景 应用加密:保护应用代码安全&…...
深入解析TOML、XML、YAML和JSON:优劣对比与场景应用
摘要:本文将介绍四种常见的配置文件和数据交换格式:TOML、XML、YAML和JSON,通过具体的使用例子分析它们的优缺点,并探讨在不同场景下的应用选择。 正文: 一、TOML 优点: 易于阅读和编写:TOML的…...
前端UniApp面试题及参考答案(100道题)
目录 UniApp 支持哪些平台? UniApp 在不同平台上的表现有何差异? 如何处理 UniApp 中的平台差异? UniApp 项目创建与目录结构 项目创建 目录结构 如何创建一个 UniApp 项目? UniApp 项目的基本目录结构是什么样的? 解释一下 UniApp 中的页面生命周期钩子函数有哪…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...