C++ 泛型编程指南02 (模板参数的类型推导)
文章目录
- 一 深入了解C++中的函数模板类型推断
- 什么是类型推断?
- 使用Boost TypeIndex库进行类型推断分析
- 示例代码
- 关键点解析
- 2. 理解函数模板类型推断
- 2.1 指针或引用类型
- 2.1.1 忽略引用
- 2.1.2 保持const属性
- 2.1.3 处理指针类型
- 2.2 万能引用类型
- 2.3 传值方式
- 2.4 传值方式的引申—std::ref与std::cref
- 2.5 数组作为实参
- 2.5.1 通过值传递
- 2.5.2 通过引用传递
- 2.6 函数名作为实参
- 2.6.1 通过值传递
- 2.6.2 通过引用传递
- 2.7 初始化列表作为实参
- 2.7.1 包含必要的头文件
- 2.7.2 定义一个接受初始化列表的模板函数
- 2.7.3 使用初始化列表调用函数
- 2.8 类型推断总结
- 2.8.1 引用类型实参的引用属性会被忽略
- 2.8.2 万能引用(通用引用)的推断依赖于实参是左值还是右值
- 2.8.3 按值传递的实参,传递给形参时`const`属性不起作用
- 2.8.4 数组或函数类型在类型推断中默认被视为指针
- 2.8.5 初始化列表必须在函数模板的形参中明确使用`std::initializer_list<T>`
- 三、现代C++的类型推导增强(深度解析)
- 1. `auto` 类型推导机制
- 基本规则
- 推导层次分析
- 工程实践要点
- 2. `decltype` 类型推导系统
- 核心行为
- 关键应用场景
- `decltype(auto)` 深度解析
- 3. 结构化绑定的类型推导(C++17)
- 基本语法形式
- 推导规则体系
- 实现原理
- 工程注意事项
- 4. 推导指南(C++17)
- 核心概念
- 自定义推导指南
- 5. 类型推导的编译时验证
- 静态断言机制
- 概念约束(C++20)
- 类型推导增强的底层原理
- 总结:现代类型推导的演进趋势
一 深入了解C++中的函数模板类型推断
在现代C++编程中,类型推断是一个极其重要的概念,特别是在模板编程领域。它不仅减少了代码的冗余度,还增强了代码的灵活性和可读性。Boost库提供了一种有效的方式来查看和理解这些类型的推断结果,这对于调试和学习都非常有帮助。
什么是类型推断?
类型推断指的是编译器根据函数调用时提供的参数自动确定模板参数的类型。这种机制允许我们编写更简洁和通用的代码,而无需显式地指定所有类型。然而,有时理解编译器是如何进行类型推断的可能并不直观,尤其是在处理引用、指针和常量等复杂情况时。
使用Boost TypeIndex库进行类型推断分析
Boost库提供了一个名为boost::typeindex::type_id_with_cvr<T>()
的功能,它可以返回一个表示类型的对象,该对象可以被用来获取类型的字符串表示,从而使得类型推断的结果更加清晰易读。
示例代码
#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T&& param)
{// 使用Boost TypeIndex库来获取并打印类型std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main()
{int x = 10;const int cx = x;const int& rx = x;myfunc(x); // 应显示 "int"myfunc(cx); // 应显示 "int const"myfunc(rx); // 应显示 "int const&"myfunc(42); // 应显示 "int"
}
关键点解析
- 安装和配置Boost:确保系统上已经安装了Boost库,并正确配置项目以包含Boost头文件路径。由于
boost::typeindex
是header-only库,因此无需链接任何特定的Boost库。 pretty_name()
方法:相比标准C++的typeid().name()
方法,pretty_name()
提供了更加人类可读的类型名称,这在调试过程中非常有用。- 不同参数的影响:通过传递不同的参数(如普通变量、常量变量、常量引用),我们可以观察到编译器如何对不同类型进行推断,这有助于深入理解模板编程中的类型推断机制。
2. 理解函数模板类型推断
2.1 指针或引用类型
在C++中,当使用函数模板时,编译器通过实参来自动推导模板参数T的类型。这个过程中,对于指针或引用类型的实参,有特定的规则需要了解。
2.1.1 忽略引用
当函数模板的实参是引用类型时,编译器在进行类型推导时会忽略掉引用部分。这意味着模板参数T不会包括引用属性。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main() {int x = 10;int& k = x;myfunc(k); // T 被推导为 int,而不是 int&
}
在这个示例中,尽管k
是对x
的引用,但是在模板推断中,T
只被推导为int
。
2.1.2 保持const属性
当传递给函数模板的引用类型形参带有const
属性的实参时,这个const
属性会影响到模板参数T的推导。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(const T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main() {const int j = 20;myfunc(j); // T 被推导为 int, 形参 param 的类型为 const int&
}
在此示例中,j
是const int
类型,T
被正确推导为int
,而形参param
的类型是const int&
。这确保了j
的常量性质不会被修改。
2.1.3 处理指针类型
对于指针类型的形参,类型推断也遵循特定的规则,尤其是在处理const
修饰符时。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T* tmprv) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name() << std::endl;
}int main() {int i = 18;const int* pi = &i;myfunc(&i); // 查看实际执行结果:T=int, tmprv=int*myfunc(pi); // 查看实际执行结果:T=int const, tmprv=int const*
}
- 当调用
myfunc(&i)
时,&i
是一个指向int
的指针,因此T
被推导为int
,而tmprv
的类型为int*
。 - 当调用
myfunc(pi)
时,pi
是一个指向const int
的指针,因此T
被推导为int const
,而tmprv
的类型为int const*
。
2.2 万能引用类型
万能引用(Universal References),也称为转发引用(Forwarding References),是一种特殊的引用类型,在模板函数中通过T&&
声明。C++的引用折叠规则使得万能引用可以根据传入的实参类型(左值或右值)来决定其最终类型:
- 绑定到左值:当一个左值被传递给万能引用时,万能引用将被推导为左值引用(
T&
)。这允许函数处理持久的对象,而不仅仅是临时值。 - 绑定到右值:当一个右值被传递给万能引用时,万能引用将被推导为右值引用(
T&&
)。这使得函数能够优化资源管理,例如通过移动语义避免不必要的复制。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void printType(T&& param) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name()<< std::endl;
}int main() {int x = 10;printType(x); // 输出x为左值的类型信息printType(10); // 输出10为右值的类型信息
}
printType(x)
会打印出T
为int&
(因为x
是一个左值)和param
也为int&
。printType(10)
会打印出T
为int
(因为10
是一个右值)和param
为int&&
。
2.3 传值方式
在C++中,当函数参数以传值方式接收时,无论原始对象是什么类型(包括指针、引用或常规变量),传递给函数的都是该对象的一个副本。这意味着在函数内部对这个副本的任何修改都不会影响到原始对象。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {int i = 18;const int j = i;const int& k = i;myfunc(i); // 实际执行结果:T=int, tmprv=intmyfunc(j); // 实际执行结果:T=int, tmprv=int,const 属性没有传递,因为对方是新副本myfunc(k); // 实际执行结果:T=int, tmprv=int,const 属性和引用都没有传递,因为对方是新副本
}
结论:
- 忽略引用性:若实参是引用类型,则引用部分会被忽略,
T
不会被推导为引用类型。 - 忽略顶层
const
:若实参是const
类型,该const
属性在类型推导时会被忽略,因为传递的是新副本。
2.4 传值方式的引申—std::ref与std::cref
为了减少不必要的数据复制,C++11提供了std::ref
和std::cref
,它们定义在<functional>
头文件中。这两个工具允许以引用的形式传递参数,而不是复制对象:
std::ref
用于创建一个对象的引用。std::cref
用于创建一个对象的常量引用。
示例代码:
#include <iostream>
#include <functional>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {int x = 10;const int y = 20;// 传值方式myfunc(x);myfunc(y);// 使用 std::ref 和 std::crefmyfunc(std::ref(x));myfunc(std::cref(y));return 0;
}
myfunc(x)
和myfunc(y)
通过值传递调用,T
被推导为int
和const int
。myfunc(std::ref(x))
和myfunc(std::cref(y))
通过引用传递调用,T
被推导为int&
和const int&
。
2.5 数组作为实参
当数组被用作函数模板的实参时,其处理方式依赖于参数传递的方式:是通过值还是通过引用。
2.5.1 通过值传递
在C++中,当数组作为函数参数通过值传递时,它不会将整个数组的副本传递给函数,而是只传递一个指向数组首元素的指针,这种现象称为“数组退化”。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char*, tmprv=const char*
}
这里,mystr
被退化为const char*
,因此T
是const char*
。
2.5.2 通过引用传递
修改函数模板使其通过引用接收参数可以保留数组的完整性,避免退化为指针。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T& tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char [14], tmprv=const char (&)[14]
}
在这种情况下,数组不会退化,T
被推导为具体的数组类型const char [14]
,而tmprv
是该数组的引用。
2.6 函数名作为实参
在C++中,函数名可以用作函数模板的实参,在编写需要回调函数的代码或实现高阶函数时,经常需要将函数名作为参数传递给其他函数。使用模板可以使这类函数更加通用和灵活。
2.6.1 通过值传递
当函数名作为实参传递时,默认被视为指向该函数的指针。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>void testFunc() {}template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc(testFunc); // 实际执行结果:T=void (*)(void), tmprv=void (*)(void)
}
这里,testFunc
被视为void (*)(void)
类型,即指向无参、无返回值函数的指针。
2.6.2 通过引用传递
通过修改模板参数为引用类型,可以获取到函数的引用,而非指针。
示例代码:
#include <iostream>
#include <boost/type_index.hpp>void testFunc() {}template <typename T>
void myfunc(T& tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc(testFunc); // 实际执行结果:T=void (&)(void), tmprv=void (&)(void)
}
在这种情况下,T
和tmprv
被推导为函数的引用类型void (&)(void)
,这显示了C++对函数的引用支持。
2.7 初始化列表作为实参
在C++中,初始化列表(std::initializer_list
)提供了一种方便的方法来处理未知数量的同类型参数。这在模板编程中尤其有用,因为它允许函数接受任意数量的同类型参数,而无需预先定义参数的数量。
2.7.1 包含必要的头文件
要使用std::initializer_list
,首先需要包含相应的头文件。通常,这会是<initializer_list>
,它定义了std::initializer_list
类。此外,为了进行输出等操作,可以包含<iostream>
头文件:
#include <initializer_list>
#include <iostream>
#include <boost/type_index.hpp> // 需要包含 Boost TypeIndex 头文件
2.7.2 定义一个接受初始化列表的模板函数
你可以定义一个模板函数,该函数接受一个类型为std::initializer_list<T>
的参数。这允许你传递一个由花括号 {}
包围的元素列表给函数,如下所示:
template <typename T>
void myfunc(std::initializer_list<T> tmprv) {for (const auto& item : tmprv) {std::cout << item << " ";}std::cout << std::endl;// 打印类型信息std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}
在这个函数中,遍历初始化列表中的每个元素,并将其打印出来。通过打印出每个元素的类型和参数的类型,可以更深入地理解类型推断和模板的行为。使用基于范围的for循环使代码简洁且易于理解。
2.7.3 使用初始化列表调用函数
在主函数中,你可以通过以下方式调用myfunc
,传入一个初始化列表:
int main() {myfunc({1, 2, 3, 4, 5}); // 调用模板函数并传入一个整数列表myfunc({"apple", "banana", "cherry"}); // 调用相同的模板函数并传入一个字符串列表
}
这样的调用方式表明,myfunc
能够接受任何类型的元素列表,只要这些元素类型相同。每次调用时,模板参数T
被推导为列表中元素的类型,无论是int
、std::string
还是其他任何类型。
2.8 类型推断总结
在C++模板编程中,类型推断遵循一些特定的规则,这些规则决定了如何根据实参推导模板参数的类型。以下是关于类型推断的一些关键点总结:
2.8.1 引用类型实参的引用属性会被忽略
在类型推断过程中,如果实参是引用类型,其引用属性会被忽略。这意味着不论实参是左值引用还是右值引用,都会被视为其底层类型进行推断。
示例:
template<typename T>
void myfunc(T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;
}int x = 10;
int& k = x;
myfunc(k); // T 被推导为 int,而不是 int&
2.8.2 万能引用(通用引用)的推断依赖于实参是左值还是右值
当模板参数按值传递时,实参的const
属性不影响推断结果,因此const
修饰符会被忽略。然而,如果传递的是指向const
的指针或引用,其const
属性仍然保留。
示例:
template<typename T>
void printType(T&& param) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name()<< std::endl;
}int main() {int x = 10;printType(x); // 输出x为左值的类型信息printType(10); // 输出10为右值的类型信息
}
printType(x)
会打印出T
为int&
(因为x
是一个左值)和param
也为int&
。printType(10)
会打印出T
为int
(因为10
是一个右值)和param
为int&&
。
2.8.3 按值传递的实参,传递给形参时const
属性不起作用
当模板参数按值传递时,实参的const
属性不影响推断结果,因此const
修饰符会被忽略。然而,如果传递的是指向const
的指针或引用,其const
属性仍然保留。
示例:
template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const int j = 20;myfunc(j); // 实际执行结果:T=int, tmprv=int,const 属性没有传递,因为对方是新副本
}
2.8.4 数组或函数类型在类型推断中默认被视为指针
在类型推断中,数组或函数名将退化为相应的指针类型,除非模板形参明确声明为引用类型,这时候不会发生退化。
示例:
template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char*, tmprv=const char*
}
2.8.5 初始化列表必须在函数模板的形参中明确使用std::initializer_list<T>
std::initializer_list
类型无法自动从花括号初始化列表推断得出,必须在函数模板的形参中显式声明为std::initializer_list<T>
类型。
示例:
template <typename T>
void myfunc(std::initializer_list<T> tmprv) {for (const auto& item : tmprv) {std::cout << item << " ";}std::cout << std::endl;// 打印类型信息std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc({1, 2, 3, 4, 5}); // 调用模板函数并传入一个整数列表myfunc({"apple", "banana", "cherry"}); // 调用相同的模板函数并传入一个字符串列表
}
三、现代C++的类型推导增强(深度解析)
现代C++(C++11及后续标准)对类型推导机制进行了重大增强,极大提升了代码的表达能力和安全性。以下从核心机制、典型应用和底层原理三个维度深入解析这些增强特性。
1. auto
类型推导机制
基本规则
- 遵循模板类型推导规则:与模板函数参数推导机制一致
- 例外处理:对初始化列表的特殊处理
auto x = 5; // int
auto y = {1, 2, 3}; // std::initializer_list<int>(C++11特有规则)
推导层次分析
-
基本推导:
const int ci = 42; auto a = ci; // int(丢弃顶层const) auto& b = ci; // const int&(保留底层const)
-
引用折叠应用:
int i = 10; auto&& r1 = i; // int&(左值推导) auto&& r2 = 42; // int&&(右值推导)
-
指针与数组处理:
const char name[] = "C++"; auto arr1 = name; // const char*(数组退化为指针) auto& arr2 = name; // const char(&)[4](保留数组类型)
工程实践要点
- 性能优化:
std::vector<std::string> heavyData; for (const auto& elem : heavyData) { ... } // 避免拷贝
- 类型精确控制:
auto ptr = static_cast<float*>(malloc(100 * sizeof(float))); // 明确指针类型
2. decltype
类型推导系统
核心行为
- 精确保留表达式类型(包括CV限定符和引用属性)
int x = 0;
const int& crx = x;
decltype(x) a; // int
decltype(crx) b; // const int&
decltype((x)) c; // int&(注意括号的影响)
关键应用场景
-
返回值类型推导:
template<typename T1, typename T2> auto add(T1 a, T2 b) -> decltype(a + b) {return a + b; }
-
类型关系维护:
template<typename Container> auto getElement(Container& c, size_t index) -> decltype(c[index]) {return c[index]; // 完美保留返回类型(可能为引用) }
-
元编程支持:
template<typename T> using RemoveReference = typename std::remove_reference<decltype(std::declval<T>())>::type;
decltype(auto)
深度解析
- 设计目标:在单表达式场景中完美转发类型信息
- 典型用例:
template<typename F, typename... Args> decltype(auto) callFunc(F&& f, Args&&... args) {return std::forward<F>(f)(std::forward<Args>(args)...); }
- 与
auto
对比:const int x = 42; auto a = x; // int decltype(auto) b = x; // const int auto& c = x; // const int& decltype(auto) d = (x);// const int&(注意括号的语义变化)
3. 结构化绑定的类型推导(C++17)
基本语法形式
auto [var1, var2, ...] = expression;
auto& [var1, var2, ...] = expression;
推导规则体系
-
绑定到非嵌套类型:
std::pair<int, std::string> p{42, "answer"}; auto& [num, text] = p; // num: int&, text: std::string&
-
绑定到结构体成员:
struct Point { double x, y; }; Point pt{1.0, 2.0}; const auto [a, b] = pt; // a: const double, b: const double
-
绑定到数组元素:
int arr[] = {1, 2, 3}; auto [x, y, z] = arr; // x,y,z: int auto& [rx, ry, rz] = arr; // rx,ry,rz: int&
实现原理
- 隐藏代理对象:编译器生成匿名结构体保存引用
- 访问器方法:实际通过
get<N>
系列函数实现访问
工程注意事项
- 生命周期管理:
auto getData() -> std::tuple<std::vector<int>, std::string>;auto [vec, str] = getData(); // vec和str是拷贝的独立对象 auto& [rvec, rstr] = getData(); // 危险!临时对象立即销毁
4. 推导指南(C++17)
核心概念
- 用户自定义推导规则:指导类模板参数推导
- 标准库应用示例:
std::vector v{1, 2, 3}; // 推导为vector<int> std::mutex mtx; std::lock_guard lck(mtx); // 推导为lock_guard<mutex>
自定义推导指南
template<typename T>
struct CustomWrapper {template<typename U>CustomWrapper(U&& u) : t(std::forward<U>(u)) {}T t;
};// 用户定义的推导指南
template<typename U>
CustomWrapper(U) -> CustomWrapper<std::decay_t<U>>;
5. 类型推导的编译时验证
静态断言机制
template<typename T>
void process(T&& param) {static_assert(std::is_integral_v<std::decay_t<T>>,"Requires integral type");
}
概念约束(C++20)
template<std::integral T>
auto safe_divide(T a, T b) -> std::optional<T> {if (b == 0) return std::nullopt;return a / b;
}
类型推导增强的底层原理
-
编译器前端处理:
- 词法分析阶段识别类型推导关键字
- 语法分析阶段构建推导上下文
-
类型系统交互:
- 结合重载决议规则(Overload Resolution)
- 引用折叠(Reference Collapsing)的实现
-
模板实例化过程:
- 两阶段查找(Two-phase lookup)的影响
- SFINAE(Substitution Failure Is Not An Error)机制
总结:现代类型推导的演进趋势
特性 | C++11 | C++14 | C++17 | C++20 |
---|---|---|---|---|
auto | 基本推导规则 | 函数返回类型推导 | 非类型模板参数推导 | 概念约束 |
decltype | 基本功能 | decltype(auto) | 结构化绑定中的推导 | 更精细的类型特征检查 |
推导指南 | N/A | N/A | 类模板参数推导 | 增强的CTAD规则 |
模式匹配 | 基础模板元编程 | 改进的SFINAE | 结构化绑定 | 模式匹配提案推进 |
通过掌握这些增强特性,开发者可以:
- 编写更简洁、类型安全的泛型代码
- 实现高效的资源管理(避免不必要的拷贝)
- 构建更灵活的接口设计
- 提升模板元编程的表达能力
- 更好地与现代C++标准库协同工作
建议通过编译器资源管理器(Compiler Explorer)实时观察不同类型推导的结果,结合标准文档深入理解各个特性的设计哲学和实现细节。
相关文章:

C++ 泛型编程指南02 (模板参数的类型推导)
文章目录 一 深入了解C中的函数模板类型推断什么是类型推断?使用Boost TypeIndex库进行类型推断分析示例代码关键点解析 2. 理解函数模板类型推断2.1 指针或引用类型2.1.1 忽略引用2.1.2 保持const属性2.1.3 处理指针类型 2.2 万能引用类型2.3 传值方式2.4 传值方式…...

音视频入门基础:RTP专题(5)——FFmpeg源码中,解析SDP的实现
一、引言 FFmpeg源码中通过ff_sdp_parse函数解析SDP。该函数定义在libavformat/rtsp.c中: int ff_sdp_parse(AVFormatContext *s, const char *content) {const char *p;int letter, i;char buf[SDP_MAX_SIZE], *q;SDPParseState sdp_parse_state { { 0 } }, *s1…...

计算机网络 应用层 笔记 (电子邮件系统,SMTP,POP3,MIME,IMAP,万维网,HTTP,html)
电子邮件系统: SMTP协议 基本概念 工作原理 连接建立: 命令交互 客户端发送命令: 服务器响应: 邮件传输: 连接关闭: 主要命令 邮件发送流程 SMTP的缺点: MIME: POP3协议 基本概念…...

【视频+图文详解】HTML基础3-html常用标签
图文教程 html常用标签 常用标签 1. 文档结构 <!DOCTYPE html>:声明HTML文档类型。<html>:定义HTML文档的根元素。<head>:定义文档头部,包含元数据。<title>:设置网页标题,浏览…...

FreeRTOS学习 --- 消息队列
队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递) 全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损 使用队列的情况如下:…...

PHP If...Else 语句详解
PHP If...Else 语句详解 引言 在PHP编程中,if...else语句是流程控制的重要组成部分,它允许程序根据条件判断执行不同的代码块。本文将详细解析PHP中的if...else语句,包括其基本用法、高级技巧以及注意事项。 一、基本用法 if...else语句的…...

pytorch使用SVM实现文本分类
人工智能例子汇总:AI常见的算法和例子-CSDN博客 完整代码: import torch import torch.nn as nn import torch.optim as optim import jieba import numpy as np from sklearn.model_selection import train_test_split from sklearn.feature_extract…...

安卓(android)读取手机通讯录【Android移动开发基础案例教程(第2版)黑马程序员】
一、实验目的(如果代码有错漏,可在代码地址查看) 1.熟悉内容提供者(Content Provider)的概念和作用。 2.掌握内容提供者的创建和使用方法。 4.掌握内容URI的结构和用途。 二、实验条件 1.熟悉内容提供者的工作原理。 2.掌握内容提供者访问其…...

【Qt】常用的容器
Qt提供了多个基于模板的容器类,这些容器类可用于存储指定类型的数据项。例如常用的字符串列表类 QStringList 可用来操作一个 QList<QString>列表。 Qt的容器类比标准模板库(standard template library,STL)中的容器类更轻巧、使用更安全且更易于使…...

基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 .…...

分布式事务组件Seata简介与使用,搭配Nacos统一管理服务端和客户端配置
文章目录 一. Seata简介二. 官方文档三. Seata分布式事务代码实现0. 环境简介1. 添加undo_log表2. 添加依赖3. 添加配置4. 开启Seata事务管理5. 启动演示 四. Seata Server配置Nacos1. 修改配置类型2. 创建Nacos配置 五. Seata Client配置Nacos1. 增加Seata关联Nacos的配置2. 在…...

JavaScript常用的内置构造函数
JavaScript作为一种广泛应用的编程语言,提供了丰富的内置构造函数,帮助开发者处理不同类型的数据和操作。这些内置构造函数在创建和操作对象时非常有用。本文将详细介绍JavaScript中常用的内置构造函数及其用途。 常用内置构造函数概述 1. Object Obj…...

25寒假算法刷题 | Day1 | LeetCode 240. 搜索二维矩阵 II,148. 排序链表
目录 240. 搜索二维矩阵 II题目描述题解 148. 排序链表题目描述题解 240. 搜索二维矩阵 II 点此跳转题目链接 题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到…...

MQTT知识
MQTT协议 MQTT 是一种基于发布/订阅模式的轻量级消息传输协议,专门针对低带宽和不稳定网络环境的物联网应用而设计,可以用极少的代码为联网设备提供实时可靠的消息服务。MQTT 协议广泛应用于物联网、移动互联网、智能硬件、车联网、智慧城市、远程医疗、…...

【机器学习与数据挖掘实战】案例11:基于灰色预测和SVR的企业所得税预测分析
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈机器学习与数据挖掘实战 ⌋ ⌋ ⌋ 机器学习是人工智能的一个分支,专注于让计算机系统通过数据学习和改进。它利用统计和计算方法,使模型能够从数据中自动提取特征并做出预测或决策。数据挖掘则是从大型数据集中发现模式、关联…...

新一代搜索引擎,是 ES 的15倍?
Manticore Search介绍 Manticore Search 是一个使用 C 开发的高性能搜索引擎,创建于 2017 年,其前身是 Sphinx Search 。Manticore Search 充分利用了 Sphinx,显着改进了它的功能,修复了数百个错误,几乎完全重写了代码…...

使用 Context API 管理临时状态,避免 Redux/Zustand 的持久化陷阱
在开发 React Native 应用时,我们经常需要管理全局状态,比如用户信息、主题设置、网络状态等。而对于某些临时状态,例如 数据同步进行中的状态 (isSyncing),我们应该选择什么方式来管理它? 在项目开发过程中ÿ…...

PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统
基于YOLOv8深度学习的学生课堂行为检测识别系统,其能识别三种学生课堂行为:names: [举手, 读书, 写字] 具体图片见如下: 第一步:YOLOv8介绍 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本…...

word2vec 实战应用介绍
Word2Vec 是一种由 Google 在 2013 年推出的重要词嵌入模型,通过将单词映射为低维向量,实现了对自然语言处理任务的高效支持。其核心思想是利用深度学习技术,通过训练大量文本数据,将单词表示为稠密的向量形式,从而捕捉单词之间的语义和语法关系。以下是关于 Word2Vec 实战…...

C# 操作符重载对象详解
.NET学习资料 .NET学习资料 .NET学习资料 一、操作符重载的概念 在 C# 中,操作符重载允许我们为自定义的类或结构体定义操作符的行为。通常,我们熟悉的操作符,如加法()、减法(-)、乘法&#…...

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理
【1】引言 前序学习进程中,对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像,每个像素点上的BGR值为三个整数,因为是三通道图像;对于灰度图像,各个像素上的BGR值是一个整数,因为这是单通…...

CNN的各种知识点(四): 非极大值抑制(Non-Maximum Suppression, NMS)
非极大值抑制(Non-Maximum Suppression, NMS) 1. 非极大值抑制(Non-Maximum Suppression, NMS)概念:算法步骤:具体例子:PyTorch实现: 总结: 1. 非极大值抑制(…...

虚幻基础16:locomotion direction
locomotion locomotion:角色运动系统的总称:包含移动、奔跑、跳跃、转向等。 locomotion direction 玩家输入 玩家输入:通常代表玩家想要的移动方向。 direction 可以计算当前朝向与移动方向的Δ。从而实现朝向与移动(玩家输入)方向的分…...

C++游戏开发实战:从引擎架构到物理碰撞
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 1. 引言 C 是游戏开发中最受欢迎的编程语言之一,因其高性能、低延迟和强大的底层控制能力,被广泛用于游戏…...

代理模式——C++实现
目录 1. 代理模式简介 2. 代码示例 1. 代理模式简介 代理模式是一种行为型模式。 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接访问引用目标对象,代理对象作为访问对象和目标…...

什么情况下,C#需要手动进行资源分配和释放?什么又是非托管资源?
扩展:如何使用C#的using语句释放资源?什么是IDisposable接口?与垃圾回收有什么关系?-CSDN博客 托管资源的回收有GC自动触发,而非托管资源需要手动释放。 在 C# 中,非托管资源是指那些不由 CLR(…...

LeetCode 2909. 元素和最小的山形三元组 II
**### LeetCode 2909. 元素和最小的山形三元组 II 问题描述 给定一个下标从 0 开始的整数数组 nums,我们需要找到一个“山形三元组”(i, j, k)满足以下条件: i < j < knums[i] < nums[j] 且 nums[k] < nums[j] 并…...

搬迁至bilibili声明
我将搬迁到bilibili ,用户名:北苏清风 在这个用户名上的文章部分将出自csdn的这个账号,均属于本人原创...

【周易哲学】生辰八字入门讲解(八)
😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本文讲解【周易哲学】生辰八字入门讲解,期待与你一同探索、学习、进步,一起卷起来叭! 目录 一、六亲女命六亲星六亲宫位相互关系 男命六亲星…...

复制粘贴小工具——Ditto
在日常工作中,复制粘贴是常见的操作,但Windows系统自带的剪贴板功能较为有限,只能保存最近一次的复制记录,这对于需要频繁复制粘贴的用户来说不太方便。今天,我们介绍一款开源、免费且功能强大的剪贴板增强工具——Dit…...