首页 > 编程语言 >浅谈右值引用 移动语义 完美转发 std::move std::forward,窥探模板元编程的一角

浅谈右值引用 移动语义 完美转发 std::move std::forward,窥探模板元编程的一角

时间:2024-12-12 10:59:03浏览次数:5  
标签:std __ 浅谈 右值 int move forward include

  右值引用 移动语义 完美转发具体是什么,就不说了,网上一搜一大堆,主要介绍下std::move和std::forward

  std::move std::forward

  查下源码,gcc版本:gcc version 7.3.0 (GCC),grep -r "forward(" /usr/include/c++/7.3.0/bits/,move和forward都在/usr/include/c++/7.3.0/bits/move.h文件中,源码如下:

  

/**
 92    *  @brief  Convert a value to an rvalue.
 93    *  @param  __t  A thing of arbitrary type.
 94    *  @return The parameter cast to an rvalue-reference to allow moving it.
 95   */
 96   template<typename _Tp>
 97     constexpr typename std::remove_reference<_Tp>::type&&
 98     move(_Tp&& __t) noexcept
 99     { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
 
 
 /**
 66    *  @brief  Forward an lvalue.
 67    *  @return The parameter cast to the specified type.
 68    *
 69    *  This function is used to implement "perfect forwarding".
 70    */
 71   template<typename _Tp>
 72     constexpr _Tp&&
 73     forward(typename std::remove_reference<_Tp>::type& __t) noexcept
 74     { return static_cast<_Tp&&>(__t); }
 75
 76   /**
 77    *  @brief  Forward an rvalue.
 78    *  @return The parameter cast to the specified type.
 79    *
 80    *  This function is used to implement "perfect forwarding".
 81    */
 82   template<typename _Tp>
 83     constexpr _Tp&&
 84     forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
 85     {
 86       static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
 87             " substituting _Tp is an lvalue reference type");
 88       return static_cast<_Tp&&>(__t);
 89     }
move forward

  本质就是强制类型转换,move并不进行所谓的“移动”

  用c++14实现一下,更简单,如下:

// C++14 version of std::move
template<typename _Tp>
constexpr decltype(auto)
move(_Tp&& __t) noexcept
{
    return static_cast<std::remove_reference_t<_Tp>&&>(__t);
}

// C++14 version of std::forward for lvalues
template<typename _Tp>
constexpr decltype(auto)
forward(std::remove_reference_t<_Tp>& __t) noexcept
{
    return static_cast<_Tp&&>(__t);
}

// C++14 version of std::forward for rvalues
template<typename _Tp>
constexpr decltype(auto)
forward(std::remove_reference_t<_Tp>&& __t) noexcept
{
    static_assert(!std::is_lvalue_reference_v<_Tp>, "template argument substituting _Tp is an lvalue reference type");
    return static_cast<_Tp&&>(__t);
}
c++14 move forward

  写了一个测试程序,如下:

  

#include <iostream>
#include <utility>  // for std::move, std::forward
#include <type_traits>  // for remove_reference_t, is_lvalue_reference_v

// C++14 version of std::move
template<typename _Tp>
constexpr decltype(auto)
move(_Tp&& __t) noexcept
{
    return static_cast<std::remove_reference_t<_Tp>&&>(__t);
}

// C++14 version of std::forward for lvalues
template<typename _Tp>
constexpr decltype(auto)
forward(std::remove_reference_t<_Tp>& __t) noexcept
{
    return static_cast<_Tp&&>(__t);
}

// C++14 version of std::forward for rvalues
template<typename _Tp>
constexpr decltype(auto)
forward(std::remove_reference_t<_Tp>&& __t) noexcept
{
    static_assert(!std::is_lvalue_reference_v<_Tp>, "template argument substituting _Tp is an lvalue reference type");
    return static_cast<_Tp&&>(__t);
}

// Test class with move and copy constructors
class Widget {
public:
    Widget() { std::cout << "Widget default constructor\n"; }

    Widget(const Widget&) {
        std::cout << "Widget copy constructor\n";
    }

    Widget(Widget&&) noexcept {
        std::cout << "Widget move constructor\n";
    }
};

// Function to test std::forward
template <typename T>
void forward_test(T&& arg) {
    Widget w = std::forward<T>(arg);
}

int main() {
    // Test std::move
    Widget widget1;
    std::cout << "Using std::move:\n";
    Widget widget2 = std::move(widget1);  // Should call move constructor

    // Test std::forward with lvalue
    std::cout << "\nUsing std::forward with lvalue:\n";
    Widget widget3;
    forward_test(widget3);  // Should call copy constructor

    // Test std::forward with rvalue
    std::cout << "\nUsing std::forward with rvalue:\n";
    forward_test(Widget());  // Should call move constructor

    return 0;
}
test

  因为is_lvalue_reference_v c++17才支持,所以编译:g++ test_move_forward.cpp -o test_move_forward -std=c++17

   标签分发

    有个全局的names,需要定义两个函数,一个是函数模板用的万能引用,一个函数的参数是普通的int(通过id检索到name,省略此实现),代码如下:

  

#include <iostream>
#include <type_traits>
#include <utility>  // for std::forward
#include <unordered_set>

// 全局数据结构
std::unordered_set<std::string> names;

// 日志函数
void log(const char* message) {
    std::cout << "Log: " << message << std::endl;
}

// 模板版本
template<typename T>
void logAndAdd(T&& name) {
    log("logAndAdd (perfect forwarding)");
    names.emplace(std::forward<T>(name));
}

void logAndAdd(int idx) {
    log("logAndAdd (int version)");
    // 处理 int 类型的逻辑
}

int main() {
    std::string name = "Alice";
    int idx = 42;

    // 测试左值
    logAndAdd(name);  // 应该调用模板版本

    // 测试右值
    logAndAdd(std::string("Bob"));  // 应该调用模板版本

    // 测试 int 类型
    logAndAdd(idx);  

    // 测试 short 类型
    short idx2 = 222;
    logAndAdd(idx2); 

    return 0;
}
标签分发

  上面的代码,没有测试 short 类型的那两行代码,是没问题的,但测试 short 类型的会匹配到完美转发那个函数,下面先用标签分发解决一下,代码如下:

  

#include <iostream>
#include <type_traits>
#include <unordered_set>
#include <chrono>
#include <utility>  // for std::forward, std::move>
#include <string>

// 全局数据结构
std::unordered_set<std::string> names;

// 日志函数
void log(const char* message) {
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    std::cout << "Log [" << std::ctime(&time) << "]: " << message << std::endl;
}

// 完美转发版本
template<typename T>
auto logAndAddImpl(T&& name) -> std::enable_if_t<
    !std::is_convertible_v<T, int>,
    void
> {
    log("logAndAdd (perfect forwarding)");
    names.emplace(std::forward<T>(name));
}

// 普通版本,专门处理 int 类型及其可隐式转换为 int 的类型
void logAndAddImpl(int idx) {
    log("logAndAdd (int version)");
    // 处理 int 类型的逻辑
    // 例如,将 int 转换为字符串并添加到集合中
    names.insert(std::to_string(idx));
}

// 分发函数
template<typename T>
void logAndAdd(T&& name) {
    if constexpr (std::is_convertible_v<T, int>) {
        logAndAddImpl(static_cast<int>(std::forward<T>(name)));
    } else {
        logAndAddImpl(std::forward<T>(name));
    }
}

// 额外的非模板版本,专门处理 int 类型
void logAndAdd(int idx) {
    logAndAddImpl(idx);
}

int main() {
    std::string name = "Alice";
    int idx = 42;
    short idx2 = 222;

    // 测试左值
    std::cout << "Testing lvalue:\n";
    logAndAdd(name);  // 应该调用完美转发版本

    // 测试右值
    std::cout << "\nTesting rvalue:\n";
    logAndAdd(std::string("Bob"));  // 应该调用完美转发版本

    // 测试 int 类型
    std::cout << "\nTesting int type:\n";
    logAndAdd(idx);  // 应该调用普通版本

    // 测试 short 类型
    std::cout << "\nTesting short type:\n";
    logAndAdd(idx2);  // 应该调用普通版本

    // 打印全局数据结构中的名字
    std::cout << "\nNames in the global set:\n";
    for (const auto& name : names) {
        std::cout << name << std::endl;
    }

    return 0;
}
标签分发2

  

  SFINAE (enable_if)

  代码如下:

  

#include <iostream>
#include <type_traits>
#include <unordered_set>
#include <chrono>
#include <utility>  // for std::forward, std::move>
#include <string>

// 全局数据结构
std::unordered_set<std::string> names;

// 日志函数
void log(const char* message) {
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    std::cout << "Log [" << std::ctime(&time) << "]: " << message << std::endl;
}

// 完美转发版本
template<typename T>
auto logAndAdd(T&& name) -> std::enable_if_t<
    !std::is_convertible_v<T, int>,
    void
> {
    log("logAndAdd (perfect forwarding)");
    names.emplace(std::forward<T>(name));
}

// 普通版本,专门处理 int 类型及其可隐式转换为 int 的类型
template<typename T>
auto logAndAdd(T&& idx) -> std::enable_if_t<
    std::is_convertible_v<T, int>,
    void
> {
    log("logAndAdd (int version)");
    // 处理 int 类型的逻辑
    // 例如,将 int 转换为字符串并添加到集合中
    names.insert(std::to_string(static_cast<int>(idx)));
}

// 额外的非模板版本,专门处理 int 类型
void logAndAdd(int idx) {
    log("logAndAdd (int version)");
    names.insert(std::to_string(idx));
}

int main() {
    std::string name = "Alice";
    int idx = 42;
    short idx2 = 222;

    // 测试左值
    std::cout << "Testing lvalue:\n";
    logAndAdd(name);  // 应该调用完美转发版本

    // 测试右值
    std::cout << "\nTesting rvalue:\n";
    logAndAdd(std::string("Bob"));  // 应该调用完美转发版本

    // 测试 int 类型
    std::cout << "\nTesting int type:\n";
    logAndAdd(idx);  // 应该调用普通版本

    // 测试 short 类型
    std::cout << "\nTesting short type:\n";
    logAndAdd(idx2);  // 应该调用普通版本

    // 打印全局数据结构中的名字
    std::cout << "\nNames in the global set:\n";
    for (const auto& name : names) {
        std::cout << name << std::endl;
    }

    return 0;
}
SFINAE

  还有一种方式模板特化,就不写代码了,写的脑壳疼

  总结

 一入模板深似海,推荐两本书:Effective Modern C++,C++ Templates,有大佬有好的书,可以评论区推荐,感谢

 

标签:std,__,浅谈,右值,int,move,forward,include
From: https://www.cnblogs.com/liudw-0215/p/18599865

相关文章

  • 浅谈C#库之MiniExcel
    一、MiniExcel的介绍    ‌是一个简单、高效的.NET库,专门用于处理Excel文件的读写和模板填充操作,旨在避免内存溢出(OOM)问题。它通过使用流(Stream)的方式进行底层算法设计,能够显著减少内存占用,通常可以将原本需要1000多MB的内存占用降低到几MB,从而有效避免内存不足的情况......
  • 浅谈C#库之Memcached
    一、Memcached的介绍        ‌Memcached‌是一个开源的分布式内存缓存系统,由LiveJournal的BradFitzpatrick开发,主要用于动态Web应用以减少数据库负载,提高访问速度和性能。Memcached通过将数据存储在内存中,使用高效的哈希算法进行数据存取,提供极高的读写性能‌。基......
  • Shell浅浅谈(六)轻松掌握基本运算符
    Shell支持多种运算符,包括:算术运算符关系运算符布尔运算符字符串运算符文件测试运算符以下是各类运算符的详细说明和实例。一.算术运算符特点使用expr命令计算表达式。表达式和运算符之间需要空格⭐。用反引号``包裹表达式。运算符说明运算符说明示例+加法expr......
  • Shell浅浅谈(三)Shell 变量与操作详解
    1.变量基础1.1定义变量语法:variable_name=value注意事项:变量名和等号之间不能有空格变量名规则:只能包含字母、数字和下划线_,例如hello_world="helloword"。不能以数字开头,例如1aaa则不行。避免使用Shell关键字,这是所有语言都需要注意的。1.2使用变量使......
  • 浅谈 Java 垃圾回收机制
    浅谈Java垃圾回收机制文章目录浅谈Java垃圾回收机制一、引言二、垃圾回收算法要解决的问题(一)内存泄漏问题(二)悬空指针问题三、常见的垃圾回收算法(一)标记-清除算法(Mark-SweepAlgorithm)(二)复制算法(CopyingAlgorithm)(三)标记-整理算法(Mark-CompactAlgorithm)(四)分代......
  • 浅谈Python+requests+pytest接口自动化测试框架的搭建
    框架的设计思路首先要明确进行接口自动化需要的步骤,如下图所示: 然后逐步拆解需要完成的工作:1)了解分析需求:了解接口要实现的功能2)数据准备:根据开发文档确定接口的基本情况,知晓接口的url、请求方式、入参等信息,然后根据业务逻辑以及入参来预期接口的输出需要有一个配置文件来......
  • [C++11] 右值引用和移动语义
    目录左值引用和右值引用左值引用与右值引用比较​编辑右值引用使用场景和意义左值引用的使用场景:右值引用和移动语义移动拷贝移动赋值右值引用引用左值及其一些更深入的使用场景分析完美转发完美转发维持值自身属性完美转发的使用场景左值引用和右值引用什么......
  • 使用std算法库:使用find算法来处理基础类型与类对象
    在C++的std库中,提供了不少基础的算法工具库,比如最基本的查找,排序等,基本上都是封装了性能极高的查找和排序算法,基本上不需要自己再去琢磨和手写各种计算机算法了,比如快排什么的,直接使用即可。不过这些算法库基本用法挺简单,在基础用法的基础上,还是有一些厉害一点的用法。基......
  • 团队冲刺-lastday
    一、站立式会议昨天已完成的任务成员任务黄英琦新增axios跨域处理,对代码进行axios请求的回应,保证前端请求能收到张怀坤验证后端功能的完整性和稳定性,包括API接口的各项功能和性能。柯锦宏针对性能问题,优化数据库查询和API响应速度。肖睿整理代码注释......
  • 浅谈C/C++内存管理
    目录一、C语言和C++内存管理方式        1、C语言和C++内存管理方式区别     2、【new】和【delete】的好处二、使用【new】来分配内存三、使用【new】来创建动态数组    1、创建动态数组        2、使用动态数组 四、使用【delete......