前言

        本文章对比了:小中大字符串在普通传值、传值移动、传左值引用、传右值引用、模板完美转发、内联版本等多种测试,对比各个方式的性能优异:

测试代码1

#include <iostream>
#include <string>
#include <chrono>
#include <utility>
#include <vector>
#include <functional>// 测量函数
template<typename Func>
double measureTime(Func&& func, int iterations) {auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {func();}auto end = std::chrono::high_resolution_clock::now();return std::chrono::duration<double, std::milli>(end - start).count();
}class Media {
public:std::string mPath;// 0. 传值void setPath0(std::string path) {mPath = path;}// 1. 传值移动void setPath1(std::string path) {mPath = std::move(path);}// 2. 传左值引用void setPath2(const std::string& path) {mPath = path;}// 3. 传右值引用void setPath3(std::string&& path) {mPath = std::move(path);}// 4. 模板完美转发template<typename T>void setPath4(T&& path) {mPath = std::forward<T>(path);}// 5. 内联版本inline void setPathInline(const std::string& path) {mPath = path;}
};// 结果存储结构
struct TestResult {std::string name;double time;
};int main() {const int N = 10000;const int WARMUP = 1000; // 预热迭代// 测试不同大小的字符串std::vector<std::pair<std::string, std::string>> testStrings = {{"小字符串(24字节)", std::string(24, 'x')},{"中等字符串(1KB)", std::string(1024, 'x')},{"大字符串(1MB)", std::string(1024 * 1024, 'x')}};for (const auto& [sizeDesc, bigString] : testStrings) {std::cout << "\n===== 测试 " << sizeDesc << " =====\n";std::vector<TestResult> results;Media media;// 预热for (int i = 0; i < WARMUP; ++i) {media.mPath = bigString;}// 0. 值传递版本测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath0(testStr);}, N);results.push_back({"setPath0(值传递,左值传参)", time});}// 1. 传值版本测试 (左值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath1(testStr);}, N);results.push_back({"setPath1(传值移动,左值传参)", time});}// 传值 + 右值传参测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath1(std::move(testStr));testStr = bigString;  // 还原数据}, N);results.push_back({"setPath1(传右值,右值传参)", time});}// 2. 传左值引用测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath2(testStr);}, N);results.push_back({"setPath2(传左值引用)", time});}// 3. 传右值引用测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath3(std::move(testStr));testStr = bigString;  // 还原数据}, N);results.push_back({"setPath3(传右值引用)", time});}// 4. 模板完美转发测试(左值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath4(testStr);}, N);results.push_back({"setPath4(模板完美转发,左值传参)", time});}// 模板完美转发测试(右值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath4(std::move(testStr));testStr = bigString;  // 还原数据}, N);results.push_back({"setPath4(模板完美转发,右值传参)", time});}// 5. 内联版本测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPathInline(testStr);}, N);results.push_back({"setPathInline(内联版本)", time});}// 6. 直接赋值测试{std::string testStr = bigString;double time = measureTime([&]() {media.mPath = testStr;}, N);results.push_back({"直接赋值", time});}// 7. 直接赋值 + 移动{std::string testStr = bigString;double time = measureTime([&]() {media.mPath = std::move(testStr);testStr = bigString;  // 还原数据}, N);results.push_back({"直接赋值 + 移动", time});}// 输出结果并排序std::sort(results.begin(), results.end(), [](const TestResult& a, const TestResult& b) {return a.time < b.time;});std::cout << "性能排名 (从快到慢):\n";for (size_t i = 0; i < results.size(); ++i) {std::cout << i+1 << ". " << results[i].name << " 耗时: " << results[i].time << " 毫秒";if (i == 0) {std::cout << " (基准)";} else {double slowdown = (results[i].time / results[0].time - 1.0) * 100.0;std::cout << " (慢 " << slowdown << "%)";}std::cout << "\n";}}return 0;
}

测试结果:

测试代码2:

#include <iostream>
#include <string>
#include <chrono>
#include <utility>
#include <vector>
#include <functional>
#include <iomanip>
#include <random>
#include <numeric>// 测量函数
template<typename Func>
double measureTime(Func&& func, int iterations) {auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < iterations; ++i) {func();}auto end = std::chrono::high_resolution_clock::now();return std::chrono::duration<double, std::milli>(end - start).count();
}//#define RESTORE_DATA  //还原数据class Media {
public:std::string mPath;void setPath0(std::string path) {mPath = path;}void setPath1(std::string path) {mPath = std::move(path);}void setPath2(const std::string& path) {mPath = path;}void setPath3(std::string&& path) {mPath = std::move(path);}template<typename T>void setPath4(T&& path) {mPath = std::forward<T>(path);}inline void setPathInline(const std::string& path) {mPath = path;}
};struct TestResult {std::string name;double time;
};// 格式化输出浮点数
std::string formatDouble(double value, int precision = 2) {std::ostringstream oss;oss << std::fixed << std::setprecision(precision) << value;return oss.str();
}int main() {const int N = 10000;const int WARMUP = 1000;std::vector<std::pair<std::string, std::string>> testStrings = {{"小字符串(24字节)", std::string(24, 'x')},{"中等字符串(1KB)", std::string(1024, 'x')},{"大字符串(1MB)", std::string(1024 * 1024, 'x')}};for (const auto& [sizeDesc, bigString] : testStrings) {std::cout << "\n===== 测试 " << sizeDesc << " =====\n";std::vector<TestResult> results;Media media;// 预热for (int i = 0; i < WARMUP; ++i) {media.mPath = bigString;}// 0. 值传递版本测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath0(testStr);}, N);results.push_back({"setPath0(值传递,左值传参)", time});}// 0. 值传递,移动语义测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath0(std::move(testStr));#ifdef RESTORE_DATAtestStr = bigString;#endif}, N);results.push_back({"setPath0(值传递,移动语义)", time});}// 1. 传值版本测试 (左值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath1(testStr);}, N);results.push_back({"setPath1(传值移动,左值传参)", time});}// 传值 + 右值传参测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath1(std::move(testStr));#ifdef RESTORE_DATAtestStr = bigString;#endif}, N);results.push_back({"setPath1(传右值,右值传参)", time});}// 2. 传左值引用测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath2(testStr);}, N);results.push_back({"setPath2(传左值引用)", time});}// 2. 传左值引用测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath2(std::move(testStr));}, N);results.push_back({"setPath2(传右值引用)", time});}// 3. 传右值引用测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPath3(std::move(testStr));#ifdef RESTORE_DATAtestStr = bigString;#endif}, N);results.push_back({"setPath3(传右值引用)", time});}// 4. 模板完美转发测试(左值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath4(testStr);}, N);results.push_back({"setPath4(模板完美转发,左值传参)", time});}// 模板完美转发测试(右值){std::string testStr = bigString;double time = measureTime([&]() {media.setPath4(std::move(testStr));#ifdef RESTORE_DATAtestStr = bigString;#endif}, N);results.push_back({"setPath4(模板完美转发,右值传参)", time});}// 5. 内联版本测试{std::string testStr = bigString;double time = measureTime([&]() {media.setPathInline(testStr);}, N);results.push_back({"setPathInline(内联版本)", time});}// 6. 直接赋值测试{std::string testStr = bigString;double time = measureTime([&]() {media.mPath = testStr;}, N);results.push_back({"直接赋值", time});}// 7. 直接赋值 + 移动{std::string testStr = bigString;double time = measureTime([&]() {media.mPath = std::move(testStr);#ifdef RESTORE_DATAtestStr = bigString;#endif}, N);results.push_back({"直接赋值 + 移动", time});}// 输出结果std::cout << "\n性能对比:\n";std::cout << std::setw(45) << std::left << "方法"<< std::setw(15) << "耗时(ms)"<< "性能比较\n";std::cout << std::string(80, '-') << "\n";double baselineTime = results[0].time; // 使用第一个测试作为基准for (const auto& result : results) {double percentage = ((baselineTime) / result.time) * 100.0;std::cout << std::setw(45) << std::left << result.name<< std::setw(15) << formatDouble(result.time);if (std::abs(percentage) < 0.1) {std::cout << "基准性能";} else if (percentage > 0) {std::cout << "快 " << formatDouble(percentage) << "%";} else {std::cout << "慢 " << formatDouble(-percentage) << "%";}std::cout << "\n";}}return 0;
}

测试结果:

需要还原数据:

不需要还原数据: 注释#define RESTORE_DATA  

结论

1. 首先注意第一点,函数内适当使用移动语义确实可以提高效率,需要还原数据时大概在30%左右,不需要还原数据时,效率提高巨大。

2. 需要注意的是,如果传递给函数的值也是move过的话,反而因为move会把原来的变量给清除,所以如果后面还需要的话,需要还原,其实效率提高并没有多少。

3. 接上一条,但是如果数据不需要了,不需要还原数据,那么效率提高将会极大,

    具体代码可以将代码开头的 #define RESTORE_DATA给注释掉。

4. 如果需要还原数据,并且兼顾代码更好写,那么左值引用是个不错的选择,还不用写还原数据:

   

     可以说,即高效又方便。比起内联函数和完美转发,效率不遑多让。

5. 如果不需要还原数据,那么下图框出来的几个都可以,setPath3右值引用兼顾代码好写和性能

6. 由数据可知,完美转发虽然写法复杂,但是兼容性好,性能高,如果掌握了其实更好用

完美转发的优劣势:

  • 完美转发能够保留参数的左值/右值属性,使函数能够根据参数的原始类型进行正确的转发,避免了不必要的拷贝和转换,提高了性能
  • 无需为左值和右值分别编写重载函数,一个模板函数就能处理多种参数类型,减少代码冗余
  • 可以编写通用的工厂函数,将参数完美转发给构造函数,便于实现代理、装饰器等设计模式
  • 完美转发会保留参数的const属性,确保类型安全
  • 库设计者可以提供更灵活的接口,而不必担心参数传递的效率问题

最适合使用完美转发的场景

  1. 通用工厂函数

  2. 转发包装器/代理函数
    • 当你需要包装一个函数,同时保留其参数的所有特性时
  3. 可变参数模板函数

    • 处理不定数量、不定类型的参数时
  4. 构建通用容器

    • 实现如emplace_back等需要直接构造对象的容器方法
  5. 中间层API设计

    • 当你的函数只是将参数传递给另一个函数,而不做任何处理时
  6. 性能关键的代码

    • 需要避免不必要拷贝的性能敏感代码

不适合使用完美转发的场景

  1. 简单的函数接口

    • 如果参数类型固定且简单,使用常规引用可能更清晰
  2. 需要明确参数类型的API

    • 当你希望API使用者明确知道参数如何传递时
  3. 教学或入门级代码

    • 对于学习者来说,完美转发可能过于复杂
  4. 需要对参数进行多次使用的场景

    • 由于右值引用可能被移动,如果需要多次使用参数,完美转发可能不是最佳选择

完美转发其他实例代码

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <type_traits>// 用于显示参数类型的辅助函数
template<typename T>
void showValueCategory(const std::string& funcName, T&& param) {std::cout << funcName << ": ";// 使用完整的类型判断if (std::is_same<T, std::remove_reference_t<T>>::value) {std::cout << "参数是右值" << std::endl;}else if (std::is_lvalue_reference<T>::value) {std::cout << "参数是左值" << std::endl;}else {std::cout << "参数是转发的右值" << std::endl;}
}//===== 示例1:字符串包装类 =====
class Message {
public:// 构造函数使用完美转发template<typename T>Message(T&& msg) : content_(std::forward<T>(msg)) {showValueCategory("Message构造", std::forward<T>(msg));// 显示更详细的类型信息std::cout << "  类型信息: " << (std::is_lvalue_reference<T>::value ? "左值引用" : std::is_rvalue_reference<T>::value ? "右值引用" : "值类型")<< std::endl;}const std::string& getContent() const { return content_; }private:std::string content_;
};//===== 示例2:工厂函数 =====
template<typename T, typename... Args>
std::unique_ptr<T> createObject(Args&&... args) {std::cout << "创建新对象...\n";// 显示每个参数的类型信息(showValueCategory("工厂函数参数", std::forward<Args>(args)), ...);return std::make_unique<T>(std::forward<Args>(args)...);
}//===== 示例3:通用打印函数 =====
class Printer {
public:template<typename T>void print(T&& value) {showValueCategory("打印函数", std::forward<T>(value));std::cout << "打印内容: " << value << std::endl;}
};//===== 示例4:参数转发容器 =====
template<typename T>
class Container {
public:template<typename Arg>void add(Arg&& arg) {showValueCategory("Container添加", std::forward<Arg>(arg));data_.emplace_back(std::forward<Arg>(arg));}void showAll() const {std::cout << "容器内容:";for (const auto& item : data_) {std::cout << item.getContent() << " ";}std::cout << std::endl;}private:std::vector<T> data_;
};// 辅助函数:显示对象移动状态
void showMove(const std::string& str) {std::cout << "String '" << str << "' 被移动" << std::endl;
}int main() {std::cout << "\n===== 完美转发示例 =====\n";// 测试1:基础构造函数转发std::cout << "\n1. 测试基础构造函数转发:" << std::endl;{std::string str = "Hello";                  // 创建左值std::cout << "传递左值:" << std::endl;Message msg1(str);                          // 传递左值std::cout << "传递右值字面量:" << std::endl;Message msg2("World");                      // 传递右值字面量std::cout << "传递移动的值:" << std::endl;Message msg3(std::move(str));              // 传递右值(移动)}// 测试2:工厂函数转发std::cout << "\n2. 测试工厂函数转发:" << std::endl;{std::string name = "Factory Object";std::cout << "\n使用左值创建:" << std::endl;auto msg1 = createObject<Message>(name);std::cout << "\n使用右值创建:" << std::endl;auto msg2 = createObject<Message>(std::string("Direct String"));std::cout << "\n使用字符串字面量创建:" << std::endl;auto msg3 = createObject<Message>("Literal String");}// 测试3:打印函数转发std::cout << "\n3. 测试打印函数转发:" << std::endl;{Printer printer;std::string text = "Left Value Text";std::cout << "打印左值:" << std::endl;printer.print(text);std::cout << "打印右值:" << std::endl;printer.print(std::string("Right Value Text"));std::cout << "打印字面量:" << std::endl;printer.print("Literal Text");}// 测试4:容器转发std::cout << "\n4. 测试容器转发:" << std::endl;{Container<Message> container;std::string item1 = "First";std::cout << "添加左值:" << std::endl;container.add(item1);std::cout << "添加右值:" << std::endl;container.add(std::string("Second"));std::cout << "添加字面量:" << std::endl;container.add("Third");container.showAll();}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C/C++ 和 OpenCV 来制作一个能与人对弈的实体棋盘机器人

项目核心架构 整个系统可以分为四个主要模块&#xff1a; 视觉感知模块 (Vision Perception Module): 任务: 使用摄像头“看懂”棋盘。工具: C, OpenCV。功能: 校准摄像头、检测棋盘边界、进行透视变换、分割 64 个棋盘格、识别每个格子上的棋子、检测人类玩家的走法。 决策模…

SpringBoot扩展——日志管理!

Spring Boot扩展 在Spring Boot中可以集成第三方的框架如MyBatis、MyBatis-Plus和RabbitMQ等统称为扩展。每一个扩展会封装成一个集成&#xff0c;即Spring Boot的starter&#xff08;依赖组件&#xff09;。starter是一种非常重要的机制&#xff0c;不需要烦琐的配置&#xf…

【JSON-To-Video】AI智能体开发:为视频图片元素添加动效(滑入、旋转、滑出),附代码

各位朋友们&#xff0c;大家好&#xff01; 今天要教大家如何在 JSON - To - Video 中为视频内图片元素添加滑入、旋转、滑出的动效。 如果您还不会封装制作自己的【视频工具插件】&#xff0c;欢迎查看之前的教程&#xff01; AI智能体平台&#xff0c;如何封装自定义短视频…

Spring Boot(九十二):Spring Boot实现连接不上数据库就重启服务

场景: 在线上部署时,若服务器因断电等原因意外重启,项目及其依赖的数据库服务通常需要配置为自动启动。此时,如果数据库服务启动较慢或失败,Spring Boot 项目会因无法建立数据库连接而启动失败。 需求: 为确保项目启动成功,需要让 Spring Boot 项目等待数据库服务完全就…

Debian配置Redis主从、哨兵

前言 Redis的下载安装可参考Centos安装配置Redis6.x&#xff0c;Centos和Debian的步骤基本类似&#xff0c;或自行在网上搜索相关资料 注意&#xff1a;远程连接需放开相应端口 主从 搭建一个一主二从的主从模式 处理conf文件 #进入redis所在目录 cd /tools/redis/redis6 …

虚实交融:数字孪生如何重塑交通与公路勘察设计的未来

当每一条道路、每一座桥梁、每一盏信号灯都在数字世界获得“永生副本”&#xff0c;交通系统从被动响应迈入主动预演的纪元 一、数字孪生的核心定义&#xff1a;超越镜像的动态认知引擎 数字孪生&#xff08;Digital Twin&#xff09;并非简单的三维可视化模型&#xff0c;而是…

vector模拟实现中的迭代器失效问题

首先来看一组代码&#xff1a; iterator insert(iterator pos, const T& x) {// 扩容if (_finish _end_of_storage){size_t len pos - _stare;reserve(capacity() 0 ? 4 : capacity() * 2);pos _stare len;}iterator end _finish - 1;while (end > pos){*(end…

java 设计模式_行为型_22模板模式

22.模板模式 模板方法&#xff08;Template Method&#xff09;作为Java的设计模式之一&#xff0c;一个词概括其优势特点那就是&#xff1a;抽象步骤 首先我们应该抽出共通的东西做一个父类&#xff08;Base类&#xff09;&#xff0c;其次具体的蛋糕制作由子类进一步实现&…

随记:在springboot中websocket的使用

我现在有两种方法 第一种&#xff1a;使用java封装的这个包下的javax.websocket.* 先配置这个配置类 import com.alibaba.nacos.common.utils.CollectionUtils; import org.springframework.stereotype.Component;import javax.websocket.HandshakeResponse; import javax.w…

技术文章大纲:SpringBoot自动化部署实战

技术文章大纲&#xff1a;SpringBoot自动化部署实战 概述 自动化部署的背景与意义SpringBoot在现代化部署中的优势常见自动化部署工具与方案概览&#xff08;Jenkins、Docker、K8s等&#xff09; 环境准备 基础工具要求&#xff1a;JDK、Maven/Gradle、Git服务器环境配置&a…

FastAdmin按钮类功能全解析 class 属性定义不同的交互行为

在 FastAdmin 中&#xff0c;超链接的 class 属性用于定义不同的交互行为和样式。以下是常见 class 配置的用途和区别&#xff1a; btn-dialog 用于触发弹出对话框行为。点击带有此 class 的链接或按钮时&#xff0c;FastAdmin 会自动加载指定的 URL 内容并在模态框中显示。通…

python3字典对象实现解析

文章目录 前言Raymond的方案字典结构字典创建字典插入插入空字典PyDictKeysObject的创建设置索引存储entry 插入非空字典调整大小字典查找联合字典插入 字典查询字典删除 前言 本来以为python字典的实现就是一个哈希表的普通实现&#xff0c;所以在学习基本类型时没去仔细研究…

Word2Vec介绍

前言 当今的大语言模型非常智能&#xff0c;但是你有没有想过这些事情&#xff1a; 机器是怎么理解“国王”和“王后”之间的关系&#xff1f; “猫”和“狗”是怎么在 AI 中“相似以及区分”的&#xff1f; 文本又是怎么变成模型能读懂的数字&#xff1f; 这一切&#xf…

Rsync+sersync实现数据实时同步(小白的“升级打怪”成长之路)

目录 一、rsync部署 push推数据 1、编写rsync配置文件 2、备份测试 3、检验结果 二、rsyncsersync 实现数据实时同步 1、安装sersync服务 2、检验结果 pull拉取数据 1、编写rsync配置文件 2、检验结果 三、脚本编写 1、客户端脚本编写 2、服务器脚本编写 一、rsy…

用 python 开发一个可调用工具的 AI Agent,实现电脑配置专业评价

在人工智能时代&#xff0c;AI Agent凭借其强大的任务处理能力&#xff0c;逐渐成为开发人员手中的得力工具。今天&#xff0c;我们就来一起动手&#xff0c;用Python打造一个能够调用工具的AI Agent&#xff0c;实现根据电脑信息对电脑配置进行专业评价的功能。 一、项目创建…

WSL 安装使用和常用命令

参考官方使用说明&#xff1a; https://learn.microsoft.com/zh-cn/windows/wsl/ 安装wsl: wsl --install --no-distribution --no-distribution&#xff1a;安装 WSL 时不要安装分发版 更新 wsl: wsl --update 设置wsl 默认版本&#xff1a; wsl --set-default-version <…

720全景VR拍摄制作实战教程

720全景VR拍摄制作实战教程 720全景VR拍摄制作是近年来兴起的一种沉浸式影像制作技术。它通过多角度拍摄&#xff0c;并将画面拼接成一个全景视角&#xff0c;使观众获得身临其境的观看体验。本教程将带你从准备阶段到拍摄阶段&#xff0c;再到后期处理阶段&#xff0c;一步步…

什么真正的云原生开发?如何区别本地开发后部署到云端?

以下是关于云原生开发的深度解析&#xff0c;以及与本地开发后迁移上云的本质区别&#xff1a; 一、真正的云原生开发&#xff1a;从理念到实践的全面革新 1. 定义与核心思想 云原生开发是一种以云计算能力为核心的架构设计和开发方法论&#xff0c;其本质是让应用从诞生之初…

从代码学习深度学习 - 词的相似性和类比任务 PyTorch版

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言加载预训练词向量TokenEmbedding 类详解预训练词向量简介 (GloVe)具体含义总结建议应用预训练词向量词相似度knn 函数get_similar_tokens 函数相似词查找示例词类比get_analogy 函数词类比任务…

ubuntu 22.04 安装部署elk(elasticsearch/logstash/kibana) 7.10.0详细教程

安装部署elk7.10.0详细教程 一、安装jdk 11环境二、安装elasticsearch 7.10.0三、安装kibana 7.10.0四、安装logstash 7.10.0五、安装ik7.10.0分词六、开启安全功能1. 开启用户名密码登录2. 开启es安全加密通信3. 开启Kibana安全功能 七、注意事项和常见错误八、其它操作及命令…