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}总结

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

相关推荐
历程里程碑1 天前
滑动窗口秒解LeetCode字母异位词
java·c语言·开发语言·数据结构·c++·算法·leetcode
Tandy12356_1 天前
手写TCP/IP协议栈——TCP结构定义与基本接口实现
c语言·网络·c++·网络协议·tcp/ip·计算机网络
Helibo441 天前
2025年12月gesp3级题解
数据结构·c++·算法
西幻凌云1 天前
初始——正则表达式
c++·正则表达式·1024程序员节
沧澜sincerely1 天前
蓝桥杯101 拉马车
c++·蓝桥杯·stl
w-w0w-w1 天前
运算符重载
c++
持梦远方1 天前
持梦行文本编辑器(cmyfEdit):架构设计与十大核心功能实现详解
开发语言·数据结构·c++·算法·microsoft·visual studio
小灰灰搞电子1 天前
C++ 文件操作详解
开发语言·c++·文件操作
im_AMBER1 天前
Leetcode 90 最佳观光组合
数据结构·c++·笔记·学习·算法·leetcode
Trouvaille ~1 天前
【C++篇】智能指针详解(一):从问题到解决方案
开发语言·c++·c++11·类和对象·智能指针·raii