现代C++、STL、QTL的使用

博客围绕现代C++展开,提及右值引用、移动语义等重要概念。还介绍了多种容器,如std::vector、std::list、std::map等,包括其操作,像std::tuple的创建、取值、解包等,最后提到std::future、std::async、std::promise用于异步编程。

C++ insight:https://cppinsights.io/
API Reference Document
bilibili 高效CPP:C++泛型编程
bilibili 合集·[现代C++基础]
在这里插入图片描述
在这里插入图片描述

0、现代C++中最重要的是:

右值引用&&、移动语义std::move、完美转发std::forward、万能引用T&&
在这里插入图片描述
在这里插入图片描述

void Func(int& x) { cout << "左值引用" << endl; }
void Func(const int& x) { cout << "const左值引用" << endl; }
void Func(int&& x) { cout << "右值引用" << endl; }
void Func(const int&& x) { cout << "const右值引用" << endl; }
 
template<typename T>
void PerfectForward(T&& t)  // 万能引用
{
    Func(std::forward<T>(t));  // 根据参数t的类型去匹配合适的重载函数
}
 
int main()
{
    int a = 4;  // 左值
    PerfectForward(a);
 
    const int b = 8;  // const左值
    PerfectForward(b);
 
    PerfectForward(10); // 10是右值
 
    const int c = 13;
    PerfectForward(std::move(c));  // const左值被move后变成const右值
 
    return 0;
}

1、std::vector、QVector

1.1 求最大值

//平均值
double getAverage(QVector<double> data) {
    return std::accumulate(data.begin(), data.end(), 0.0) / data.size();
}
 
//最大值
double getMax(QVector<double> data) {
    return *std::max_element(std::begin(data), std::end(data));
}
 
//最小值
double getMin(QVector<double> data) {
    return *std::min_element(std::begin(data), std::end(data));
}
 
//标准差, 也称均方差, 是方差的算术平方根
double getStd(QVector<double> data) {
    double mean = getAverage(data);
    double accum = 0.0;
    std::for_each (std::begin(data), std::end(data), [&](const double d) {
        accum += (d-mean) * (d-mean);
    });
    return sqrt(accum / data.size()); //除以n-1是无偏估计方差, 除以n是概率分布方差
}

QVector <double> data {11.0, 44.0, 22.0, 33.0, 10.0,65.0};

//求最大值最小值:返回的是迭代器QVector<double>::iterator max
auto max = std::max_element(std::begin(data), std::end(data));
auto min = std::min_element(std::begin(data), std::end(data));
double biggest = *max;
double smallest = *min;

//最大值和最小值的位置:
auto positionmax = std::distance(std::begin(data),max);
auto positionmin = std::distance(std::begin(data),min);
int posmax = positionmax;
int posmin = positionmin;

qDebug()<<"biggest = "<<biggest;
qDebug()<<"smallest = "<<smallest;
qDebug()<<"pos ="<<posmax;
qDebug()<<"posmin = "<<posmin;

2、std::list、QList

3、std::map、QMap

4、std::multimap、QMultiMap

5、std::tuple

(1)创建:

std::tuple<T1, T2, TN> t1;  				//创建一个空的tuple对象(使用默认构造),它对应的元素分别是T1和T2...Tn类型,采用值初始化。
std::tuple<T1, T2, TN> t2(v1, v2, ... TN);  //创建一个tuple对象,它的元素分别是T1和T2 ...Tn类型; 要获取元素的值需要通过tuple的成员get<Ith>(obj)进行获取(Ith是指获取在tuple中的第几个元素,请看后面具体实例)。
std::tuple<T1&> t3(ref&); 					// tuple的元素类型可以是一个引用
std::make_tuple(v1, v2); 					// 像pair一样也可以通过make_tuple进行创建一个tuple对象

(2)取值: std::get()
tuple不支持迭代,只能通过元素索引(或tie解包)进行获取元素的值。

{
    int n = 1;
    auto t = std::make_tuple(10, "Test", 3.14, std::ref(n), n);
    // get尖括号中的值必须是一个整型常量表达式。从0开始计数,意味着get<0>是第一个成员。
    std::cout << "The value of t is "  << "("
               << std::get<0>(t) << ", " << std::get<1>(t) << ", "
               << std::get<2>(t) << ", " << std::get<3>(t) << ", "
               << std::get<4>(t) << ")\n";
    // 由于get返回指定元素的引用,所以可用来修改指定位置的元素的值。此处因为第4个元素是引用类型,所以被引用的值也会改变
    std::get<3>(t) = 9;
    std::cout << n << std::endl;
}

(3)解包: std::tie()

int myint;
char mychar;
std::tuple<int, float, char> mytuple;
mytuple = std::make_tuple(10, 2.6, 'a');          // packing values into tuple

std::tie(myint, std::ignore, mychar) = mytuple;   // unpacking tuple into variables
std::cout << "myint contains: " << myint << '\n';
std::cout << "mychar contains: " << mychar << '\n';

(4)元素个数: std::tuple_size<>()

std::tuple<int, char, double> mytuple (10, 'a', 3.14); 
int count = std::tuple_size<decltype(mytuple)>::value;		//3

(5)元素类型: std::tuple_element<index, tuple>

std::tuple<int, std::string> third(9, std::string("ABC"));       
std::tuple_element<1, decltype(third)>::type  val_1;		// 得到元组第1个元素的类型,用元组第一个元素的类型声明一个变量

(6)拼接

std::tuple<char, int, double> first('A', 1, 2.2f);
auto second = std::tuple_cat(first, std::make_tuple('B', std::string("-=+")));
std::tuple<char, int, double, char, std::string> third = std::tuple_cat(first, std::make_tuple('B', std::string("-=+")));

(7)Qt中的实际应用
实际应用:

#include <tuple>
#include <QVector>
std::tuple<quint16, quint16, quint16, QVector<quint16>, QVector<quint16>> myFunction()
{
    quint16 data1 = 10;
    quint16 data2 = 20;
    quint16 data3 = 30;
    QVector<quint16> array1{1, 2, 3};
    QVector<quint16> array2{4, 5, 6};
    return std::make_tuple(data1, data2, data3, array1, array2);
}
int main()
{
    auto result = myFunction();
    quint16 returnedData1 = std::get<0>(result);
    quint16 returnedData2 = std::get<1>(result);
    quint16 returnedData3 = std::get<2>(result);
    QVector<quint16> returnedArray1 = std::get<3>(result);
    QVector<quint16> returnedArray2 = std::get<4>(result);
    // 使用返回的数据进行后续操作...
    return 0;
}

6、std::future、std::async、std::promise

C/C++开发基础——std::future与async异步编程

7、可变参数

《深入应用C++11》P103、P127、P159

7.1 函数

1、定义:

template <class... T>
void f(T... args)
{
}

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

2、调用另一个函数

//《深入应用C++11》P127
template<typename R, typename... Args>
class Task<R(Args...)>
{
public:
	Task(std::function<R(Args...)>&& f):m_fn(std::move(f)){};
	Task(std::function<R(Args...)>&  f):m_fn(f){};
	R Run(Args&&... args)
	{
		return m_fn(std::forward<Args>(args)...);
	}
private:
	std::function<R(Args...)> m_fn;
}

3、传递:
以自定义make_unique为例,如果不是数组,则直接创建nuique_ptr,如果是数组,先判断是否为定长数组,如果动态数组,则获取数组元素类型,再根据入参(长度)创建动态数组,若是定长数组则编译不通过。

//普通指针
template<class T, class... Args> inline
typename enable_if<!is_array<T>::value, unique_ptr<T>>::type
make_unique(Args&&... args)
{
	return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
//动态数组
template<class T> inline
typename enable_if<is_array<T>::value && extent<T>::value==0, unique_ptr<T>>::type
make_unique(size_t size)
{
	typedef typename remove_extent<T>::type U;
	return unique_ptr<T>(new U[size]());
}
//定长数组
template<class T, class... Args>
typename enable_if<extent<T>::value!=0, void>::type make_unique(Args&&...) = delete;

改进观察者模式:P226

//通知所有观察者
template<typename... Args>
void Notify(Args&&... args)							//声明变参
{
	for(auto& it: m_connections)
	{
		it.second(std::forward<Args>(args)...);		//传递参数
	}
}

4、函数和参数传递
回调:

#include <utility>		//for std::invoke()
#include <functional>	//for std::forward()

template<typename Callable, typename... Args>
decltype(auto) call(Callable&& op, Args&&... args)
{
	return std::invoke(std::forward<Callable>(op), std::forward<Args>(args)...);
}

8、enable_if

C++11模板元编程—std::enable_if使用说明
用作函数参数、返回类型或类模板或函数模板参数,以有条件地从重载中删除函数或类

9、std::extent、std::remove_extent

数组维度的操作:

std::extent<T>::value  //获取数组在指定维度上的大小
std::remove_extent<T>::type U;	//移除一个维度,即数组元组的类型为U

10、std::transform

转换有两种:一元转换和二元转换。
一元转换是对容器给定范围内的每个元素做某种一元运算后放在另一个容器里。只涉及一个参与转换运算的容器。
有4个参数,前2个指定要转换的容器的起止范围,第3个参数是结果存放容器的起始位置,第4个参数是一元运算。

#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    std::string str = "Hello, World!";
    cout << str << endl;

    // toupper
    std::string strUpper(str.length(), ' ');
    std::transform(str.begin(), str.end(), strUpper.begin(), std::toupper);
    cout << strUpper << endl;

    // tolower
    std::transform(str.begin(), str.end(), str.begin(), std::tolower);
    cout << str << endl;

    system("pause");
    return 0;
}
输出:
Hello, World!
HELLO, WORLD!
hello, world!

二元转换是对两个容器给定范围内的每个元素做二元运算后放在另一个容器里。涉及两个参与转换运算容器。
有5个参数,前2个指定参与转换的第1个容器的起止范围,第3个参数是转换的第2个容器的起始位置,
第4个参数是结果存放容器的起始位置,第5个参数是二元运算。

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<int> v1 = {10, 20, 30, 40, 50};
    std::vector<int> v2 = { 1, 2, 3, 4, 5 };

    std::vector<int> result(5);
    std::transform(v1.begin(), v1.end(), v2.begin(), result.begin(), std::plus<int>());

    for (int i : result) {
        std::cout << i << "\t";
    }
    std::cout << std::endl;

    return 0;
}
输出: 11      22      33      44      55

11、std::partition_copy

使用std::partition_copy配合一个捕获索引的lambda函数来实现按照元素位置(次序)的奇偶性分离数据。
按次序分离:

#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::vector<int> even_positions; // 存储偶数位置(索引为偶)的元素
    std::vector<int> odd_positions;  // 存储奇数位置(索引为奇)的元素
    
    // 使用partition_copy分离数据
    std::partition_copy(data.begin(), data.end(),
                       std::back_inserter(even_positions),
                       std::back_inserter(odd_positions),
                       [index = 0](const auto& element) mutable {
                           // 注意:索引从0开始,所以索引0是第一个元素(偶数位置)
                           // 索引1是第二个元素(奇数位置),以此类推
                           return index++ % 2 == 0;
                       });
}
//lambda函数中的索引捕获:[index = 0] 初始化一个索引计数器
//mutable关键字:允许在lambda函数内修改捕获的index变量

按内容(值)来分离:

#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>

bool is_even(int n) {
return n % 2 == 0;
}

int main() {
std::vector<int> input = {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> evens(input.size());
std::vector<int> odds(input.size());

auto result = std::partition_copy(input.begin(), input.end(),
					evens.begin(), odds.begin(), is_even
				);

std::cout << "Evens: ";
for (auto it = evens.begin(); it != result.first; ++it) {
std::cout << *it << " ";
}
std::cout << "\nOdds: ";
for (auto it = odds.begin(); it != result.second; ++it) {
std::cout << *it << " ";
}
return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值