C++23新特性_简化隐士移动和auto{}

文章目录

  • [第一章 C++23语言特性](#第一章 C++23语言特性)
    • [1.5 简化隐式移动](#1.5 简化隐式移动)
    • [1.6 auto(x) 和 auto{x}](#1.6 auto(x) 和 auto{x})
      • [1.6.1 语法格式](#1.6.1 语法格式)
      • [1.6.2 auto{}的使用](#1.6.2 auto{}的使用)
      • [1.6.3 auto{x}总结](#1.6.3 auto{x}总结)

本文介绍C++23新特性之简化隐士移动和auto{}。

第一章 C++23语言特性

1.5 简化隐式移动

在 C++23 之前,编译器在 return x; 时自动执行 move(而不是 copy)是有严格条件的。如果条件不满足,必须手动调用move:

需要手动move的场景1:如果返回的类型和局部变量的类型不完全匹配。

cpp 复制代码
    struct Base
    {
        ~Base()
        {

        }
    };

    struct Derived : public Base
    {
        ~Derived()
        {
        }
	};
    // C++20 及以前
    static std::unique_ptr<Base> create()
    {
        auto ptr = std::make_unique<Derived>();
        // 错误!编译器不敢隐式 move,因为 Derived* 和 Base* 类型不同
        // return ptr; 

        // 必须显式写:
        return std::move(ptr);
    }

    void test()
    {
        auto bptr = create();
	}

场景2:如果局部变量本身就是一个右值引用,之前的标准中认为它是一个"左值表达式",因此在返回时会尝试拷贝,除非再次move它。

cpp 复制代码
// C++20 及以前
std::string forward_str(std::string&& s) {
    // 错误!s 虽然是右值引用类型,但 s 变量本身是左值
    // return s; // 尝试拷贝,如果 string 不可拷贝则报错
    
    // 必须显式写:
    return std::move(s);
}

C++23的解决方案,放宽原则。只要是 return 语句中的局部变量(包括参数),且该变量即将销毁,编译器就会默认将其视为右值 (xvalue),自动尝试移动构造,无需手动 std::move。

示例1:返回右值引用参数

cpp 复制代码
std::unique_ptr<int> pass_through(std::unique_ptr<int>&& ptr) {
    // C++20: 编译失败!
    // ptr 有名字,所以是左值。unique_ptr 不可拷贝。
    // 必须写 return std::move(ptr);
    
    // C++23: 编译通过!✅
    // 编译器知道 ptr 是参数,且马上要返回,自动 treat as rvalue。
    return ptr; 
}

示例2:工厂模式与多态

在工厂函数中,我们经常创建子类对象但返回基类指针(或智能指针)。

cpp 复制代码
class Base {};
class Derived : public Base {};

std::unique_ptr<Base> create_instance() {
    auto derived = std::make_unique<Derived>();
    
    // 做一些针对 Derived 的初始化操作...
    // derived->do_something();

    // C++23 之前:必须 return std::move(derived);
    // C++23:直接返回,自动处理类型转换 + 移动
    return derived; 
}

1.6 auto(x) 和 auto{x}

在C++23之前,编程时出现的两种问题:

问题1:lambda中的悬挂引用。在使用 Lambda 表达式或协程时,如果我们按引用捕获了一个临时对象,或者直接使用了参数的引用,可能会导致悬挂引用,这时我们需要一种方式强制拷贝一份数据保存下来。

问题2:泛型编程中的类型玻璃。泛型编程中的类型剥离在模板代码中,传入的参数可能是 const int&。如果我们想把这个值传给一个只接受右值的函数,或者想去掉 const 和引用属性得到原始类型 int,以前的需要使用decay手段去掉属性。

cpp 复制代码
void func(const std::string& str) {
    // 想得到 str 的一个非 const 副本
    
    // 写法 1: 显式类型(不通用,如果 str 类型变了要改代码)
    std::string copy1 = str; 
    
    // 写法 2: auto 变量(多占一行,打断了表达式的流畅性)
    auto copy2 = str;
    process(std::move(copy2));
    
    // 写法 3: 复杂的 cast(难以阅读)
    process(std::decay_t<decltype(str)>(str)); 
}

C++23中,直接使用auto(x),获得x的一个副本,并且这个副本是右值。

1.6.1 语法格式

auto{x} 和 auto(x) :复制一份x,复制的这份x后,auto{x}返回的是纯右值。得到的效果如下:

cpp 复制代码
int val = 42;
const int& ref = val;

// copy 的类型是 int(去除了 const 和 &)
// copy 是一个右值
process(auto(ref)); 

1.6.2 auto{}的使用

下面代码中 take_owership函数接收一个右值。分别使用move() 和 auto{}方式实现。使用auto{data}实现的好处是,不破坏之前的资源所有权。

cpp 复制代码
    void take_owership(std::vector<int>&& v)
    {

    }

    void test()
    {
        std::vector<int> data = { 1,2,3,4,5 };
        // 错误!不能将左值传递给右值引用参数
        // take_owership(data); 
        
        // 正确:使用 std::move 将左值转换为右值
		take_owership(std::move(data));

        // C++23做法
        take_owership(auto{ data }); // 自动将 data 视为右值

		for (int x : data)
        {
            std::cout << x << " ";
        }
        // 1 2 3 4 5
    }

1.6.3 auto{x}总结

这是一种语法格式,既传递了右值,又不破坏之前的所有权,让代码变得更简洁。

相关推荐
小小晓.4 小时前
Pinely Round 4 (Div. 1 + Div. 2)
c++·算法
SHOJYS4 小时前
学习离线处理 [CSP-J 2022 山东] 部署
数据结构·c++·学习·算法
steins_甲乙4 小时前
C++并发编程(3)——资源竞争下的安全栈
开发语言·c++·安全
煤球王子5 小时前
学而时习之:C++中的异常处理2
c++
仰泳的熊猫5 小时前
1084 Broken Keyboard
数据结构·c++·算法·pat考试
我不会插花弄玉5 小时前
C++的内存管理【由浅入深-C++】
c++
CSDN_RTKLIB5 小时前
代码指令与属性配置
开发语言·c++
上不如老下不如小5 小时前
2025年第七届全国高校计算机能力挑战赛 决赛 C++组 编程题汇总
开发语言·c++
雍凉明月夜5 小时前
c++ 精学笔记记录Ⅱ
开发语言·c++·笔记·vscode
GHL2842710906 小时前
文件重命名(C++源码)
前端·c++·windows