C++23 std::move_only_function:一种仅可移动的可调用包装器 (P0288R9)

文章目录

    • 一、定义与基本概念
      • [1.1 定义](#1.1 定义)
      • [1.2 基本概念](#1.2 基本概念)
    • 二、特点
      • [2.1 仅可移动性](#2.1 仅可移动性)
      • [2.2 支持多种限定符](#2.2 支持多种限定符)
      • [2.3 无`target_type`和`target`访问器](#2.3 无target_typetarget访问器)
      • [2.4 强前置条件](#2.4 强前置条件)
    • 三、使用场景
      • [3.1 处理不可复制的可调用对象](#3.1 处理不可复制的可调用对象)
      • [3.2 性能优化](#3.2 性能优化)
      • [3.3 资源管理](#3.3 资源管理)
    • 四、与其他可调用包装器的对比
      • [4.1 与`std::function`的对比](#4.1 与std::function的对比)
      • [4.2 与`std::function_ref`的对比](#4.2 与std::function_ref的对比)
    • 五、总结

在C++的发展历程中,不断引入新的特性和工具来提升语言的性能和灵活性。C++23标准引入了 std::move_only_function,这是一种仅可移动的可调用包装器,为开发者处理不可复制的可调用对象提供了强大的支持。本文将详细介绍 std::move_only_function的定义、特点、使用场景以及与其他可调用包装器的对比。

一、定义与基本概念

1.1 定义

std::move_only_function是一个通用的多态函数包装器,定义于头文件<functional>中。它可以存储和调用任何可构造的(不要求是可移动构造的)可调用目标,包括函数、lambda表达式、bind表达式、其他函数对象,以及指向成员函数的指针和指向成员对象的指针。存储的可调用对象被称为std::move_only_function的目标。如果std::move_only_function不包含目标,则被称为空的。与std::function不同,调用空的std::move_only_function会导致未定义行为。

其模板定义如下:

cpp 复制代码
// 主模板未定义
// template< class... >
// class move_only_function; 

// 各种特化版本
// template< class R, class... Args >
// class move_only_function< R( Args...)> ;
// template< class R, class... Args >
// class move_only_function< R( Args...) noexcept>;
// template< class R, class... Args >
// class move_only_function< R( Args...) &> ;
// template< class R, class... Args >
// class move_only_function< R( Args...) & noexcept>;
// template< class R, class... Args >
// class move_only_function< R( Args...) &&>;
// template< class R, class... Args >
// class move_only_function< R( Args...) && noexcept>;
// template< class R, class... Args >
// class move_only_function< R( Args...) const> ;
// template< class R, class... Args >
// class move_only_function< R( Args...) const noexcept>;
// template< class R, class... Args >
// class move_only_function< R( Args...) const &> ;
// template< class R, class... Args >
// class move_only_function< R( Args...) const & noexcept> ;
// template< class R, class... Args >
// class move_only_function< R( Args...) const &&>;
// template< class R, class... Args >
// class move_only_function< R( Args...) const && noexcept >;

1.2 基本概念

std::move_only_function满足MoveConstructibleMoveAssignable的要求,但不满足CopyConstructibleCopyAssignable。这意味着它只能通过移动操作进行构造和赋值,不能进行复制操作。这种设计使得std::move_only_function能够处理那些不可复制的可调用对象,例如捕获了std::unique_ptr的lambda表达式。

二、特点

2.1 仅可移动性

std::move_only_function的核心特点是仅可移动。这是为了处理那些不可复制的可调用对象而设计的。在C++中,有些对象由于其资源管理的特性(如std::unique_ptr)不能被复制,只能被移动。std::move_only_function允许我们将这些不可复制的可调用对象存储在一个包装器中,从而方便地进行传递和调用。

2.2 支持多种限定符

std::move_only_function支持模板参数中提供的每一种可能的cv限定符(不包括volatile)、引用限定符和noexcept说明符的组合。这些限定符和说明符(如果有的话)会被添加到其operator()中。这使得std::move_only_function能够精确地匹配不同类型的可调用对象,提供了更高的灵活性。

2.3 无target_typetarget访问器

std::function不同,std::move_only_function没有target_typetarget访问ors。这是根据用户和实现者的需求做出的设计决策。移除这些访问器可以简化实现,并避免一些潜在的问题。

2.4 强前置条件

调用std::move_only_function时具有强前置条件。如果std::move_only_function为空,则调用它会导致未定义行为。这要求开发者在调用之前确保std::move_only_function包含有效的目标。

三、使用场景

3.1 处理不可复制的可调用对象

当需要处理捕获了不可复制对象(如std::unique_ptr)的lambda表达式时,std::move_only_function是一个很好的选择。以下是一个示例:

cpp 复制代码
#include <iostream>
#include <functional>
#include <memory>

int main() {
    auto ptr = std::make_unique<int>(42);
    // 捕获std::unique_ptr的lambda表达式
    auto lambda = [ptr = std::move(ptr)]() {
        std::cout << *ptr << std::endl;
    };
    // 使用std::move_only_function存储lambda表达式
    std::move_only_function<void()> func = std::move(lambda);
    func(); // 调用存储的lambda表达式
    return 0;
}

在这个示例中,lambda表达式捕获了一个std::unique_ptr,由于std::unique_ptr是不可复制的,因此不能使用std::function来存储这个lambda表达式。而std::move_only_function可以通过移动操作来存储这个lambda表达式,从而实现对不可复制可调用对象的处理。

3.2 性能优化

在某些情况下,使用std::move_only_function可以避免不必要的复制操作,从而提高性能。例如,当需要将一个可调用对象从一个函数传递到另一个函数时,如果这个可调用对象是不可复制的,使用std::move_only_function可以通过移动操作来传递,避免了复制的开销。

3.3 资源管理

std::move_only_function可以用于管理资源的生命周期。例如,在一个函数中创建一个可调用对象,该对象持有一些资源(如文件句柄、网络连接等),并将其存储在std::move_only_function中。当std::move_only_function被销毁时,其持有的可调用对象也会被销毁,从而自动释放资源。

四、与其他可调用包装器的对比

4.1 与std::function的对比

  • 复制性std::function是可复制的,而std::move_only_function是仅可移动的。这意味着std::function可以存储和复制任何可复制的可调用对象,而std::move_only_function专门用于处理不可复制的可调用对象。
  • 性能 :由于std::move_only_function避免了复制操作,在处理不可复制对象时可能具有更好的性能。而std::function在复制操作上可能会有一定的开销。
  • 功能完整性std::function具有target_typetarget访问ors,可以用于获取存储的可调用对象的类型和指针。而std::move_only_function没有这些访问ors,这使得它的实现更加简单,但也限制了一些功能。

4.2 与std::function_ref的对比

  • 所有权std::function_ref是一个轻量级的引用包装器,它不拥有可调用对象的所有权,只是对可调用对象的一个引用。而std::move_only_function拥有可调用对象的所有权,当std::move_only_function被销毁时,其持有的可调用对象也会被销毁。
  • 生命周期管理 :由于std::function_ref只是一个引用,它要求被引用的可调用对象的生命周期必须长于std::function_ref本身。而std::move_only_function对可调用对象的生命周期有完全的控制权。
  • 可移动性std::function_ref是可复制的,而std::move_only_function是仅可移动的。这使得std::function_ref更适合作为函数参数传递,而std::move_only_function更适合用于存储和管理不可复制的可调用对象。

五、总结

std::move_only_function是C++23标准引入的一个重要特性,它为开发者提供了一种处理不可复制可调用对象的有效方式。通过其仅可移动的特性、对多种限定符的支持以及强前置条件等特点,std::move_only_function在处理复杂的可调用对象和优化性能方面具有很大的优势。在实际开发中,当需要处理不可复制的可调用对象时,不妨考虑使用std::move_only_function来提升代码的性能和灵活性。

相关推荐
oioihoii13 小时前
C++23 std::invoke_r:调用可调用 (Callable) 对象 (P2136R3)
开发语言·c++23
oioihoii2 天前
C++23 std::byteswap:反转字节 (P1272R4)
c++23
oioihoii3 天前
C++23文本编码革新:迈向更现代的字符处理
java·数据库·c++23
oioihoii4 天前
C++23 新特性:令声明顺序决定非静态类数据成员的布局 (P1847R4)
java·开发语言·c++23
撸码到无法自拔4 天前
C++23中if consteval / if not consteval (P1938R3) 详解
c++23
大G哥5 天前
C++23中if consteval / if not consteval (P1938R3) 详解
c++23
oioihoii5 天前
C++23 中 static_assert 和 if constexpr 的窄化布尔转换
java·jvm·c++23
oioihoii5 天前
C++23 中 constexpr 的重要改动
c++·算法·c++23
oioihoii6 天前
C++23中if consteval / if not consteval (P1938R3) 详解
java·数据库·c++23