runoob_26_utility

/*
 * @Author: by777
 * @Date: 2025-03-24 17:56:04
 * @LastEditors: by777
 * @LastEditTime: 2025-03-24 18:15:28
 * @FilePath: /cxx_stl/runoob_26.cpp
 * @Description: https://www.runoob.com/cplusplus/cpp-libs-utility.html
 * 
 * Copyright (c) 2025 by by777, All Rights Reserved. 
 */
#include <iostream>
#include <vector>
#include <utility> // 文件定义了多种工具类和函数,它们主要用于简化编程任务,提高代码的可读性和可维护性
using namespace std;

/*
 *pair:一个包含两个元素的容器,通常用于存储和返回两个相关联的值。
 *make_pair:一个函数模板,用于创建 pair 对象。
 *swap:一个函数模板,用于交换两个对象的值。
 *forward 和 move:用于完美转发和移动语义的函数模板
*/
void process(int& i) {
    std::cout << "Lvalue reference to " << i << std::endl;
}

void process(int&& i) {
    std::cout << "Rvalue reference to " << i << std::endl;
}

template <typename T>
void forward_example(T&& t) {
    process(std::forward<T>(t));
}
   
int main(int argc, const char** argv) {
    
    {
        auto p = std::make_pair(10, 20);
        // 输出 pair 对象的元素
        std::cout << "First element: " << p.first << std::endl;
        std::cout << "Second element: " << p.second << std::endl;
    }
    {
        int a = 5;
        int b = 10;
        std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
        // 使用 swap 函数交换 a 和 b 的值
        std::swap(a, b);
        std::cout << "After swap: a = " << a << ", b = " << b << std::endl;
    }
    {
        // std::move 将一个对象转换为右值引用(rvalue reference),从而允许移动操作而不是拷贝操作。
        std::vector<int> v1 = {1, 2, 3, 4, 5};
        std::vector<int> v2 = std::move(v1);
        std::cout << "v1 size: " << v1.size() << std::endl; // v1 现在为空
        std::cout << "v2 size: " << v2.size() << std::endl; // v2 拥有 v1 的元素
        /*
        关键点

        移动语义:
            std::move 允许将资源从一个对象转移到另一个对象,而不是进行深拷贝。
            移动操作通常比拷贝操作更高效,因为它只是转移资源的所有权,而不是复制资源。
        右值引用:
            std::move 将对象转换为右值引用,从而启用移动构造函数或移动赋值运算符。
            右值引用是临时值,可以被移动但不能被拷贝。
        性能优化:
            在处理大型对象(如字符串、向量、映射等)时,移动语义可以显著提高性能,减少不必要的内存分配和拷贝。

        常见应用场景

        返回大型对象:
            在函数返回大型对象时,使用 std::move 可以避免拷贝,提高性能。
        */

    }

    {
        // 使用 forward 函数
        int x = 10;
        // orward_example(x):x 是一个左值,T 被推导为 int&,std::forward<int&>(x) 保持左值引用,调用左值 process 函数
        forward_example(x);           // Lvalue reference
        // orward_example(20):20 是一个右值,T 被推导为 int,std::forward<int>(20) 转换为右值引用,调用右值 process 函数。
        forward_example(20);          // Rvalue reference
        // orward_example(std::move(x)):std::move(x) 将 x 转换为右值,T 被推导为 int,
        // std::forward<int>(std::move(x)) 转换为右值引用,调用右值 process 函数。
        forward_example(std::move(x));// Rvalue reference
        /**
        “如果有变量名就是左值,如果是立即数就是右值”,这种规则在某些情况下是成立的,但并不是完全准确的。
        例如,变量名通常代表左值,因为它们有持久的生命周期,而立即数(如字面量)通常是右值,因为它们是临时的。
        然而,这种判断方式有例外,比如当变量被std::move处理后,它也会被视为右值。
        在 C++ 中,值类别(value category)是用于区分表达式是左值(lvalue)还是右值(rvalue)的概念。你的理解在大多数情况下是正确的,但有一些例外和更细致的规则。
        左值:
        
            定义:左值是指具有名称的变量,其生命周期通常超过表达式的作用域。
            特点:
                可以出现在赋值运算符的左侧。
                有固定的内存地址。
                生命周期较长,通常直到变量的作用域结束。
                右值(rvalue)
                    int x = 10;
                    process(x); // x 是左值
        右值:
            定义:右值是指临时对象或字面量,其生命周期通常仅限于表达式的作用域。
            特点:
            不能出现在赋值运算符的左侧。
            没有固定的内存地址。
            生命周期较短,通常在表达式结束时销毁。
            process(20); // 20 是右值

        例外情况

            1. std::move:可以将一个左值转换为右值引用,从而使其在表达式中被视为右值。
            2. 函数返回值:函数返回的临时对象通常是右值。
            3. 字面量:如 42、3.14、"hello",通常是右值
        总结:
            1. 有变量名的通常是左值:如果一个表达式有名称(如变量名),它通常是左值。
            2. 立即数通常是右值:字面量和临时对象通常是右值。
            3. std::move 可以改变值类别:std::move 可以将左值转换为右值引用。
        */
    }
    return 0;
}

posted @ 2025-03-25 10:54  白柒  阅读(6)  评论(0)    收藏  举报