C++23 std::move_only_function:一种仅可移动的可调用包装器 (P0288R9)
文章目录
- 一、定义与基本概念
- 1.1 定义
- 1.2 基本概念
- 二、特点
- 2.1 仅可移动性
- 2.2 支持多种限定符
- 2.3 无`target_type`和`target`访问器
- 2.4 强前置条件
- 三、使用场景
- 3.1 处理不可复制的可调用对象
- 3.2 性能优化
- 3.3 资源管理
- 四、与其他可调用包装器的对比
- 4.1 与`std::function`的对比
- 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
会导致未定义行为。
其模板定义如下:
// 主模板未定义
// 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
满足MoveConstructible
和MoveAssignable
的要求,但不满足CopyConstructible
或CopyAssignable
。这意味着它只能通过移动操作进行构造和赋值,不能进行复制操作。这种设计使得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_type
和target
访问器
与std::function
不同,std::move_only_function
没有target_type
和target
访问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
是一个很好的选择。以下是一个示例:
#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_type
和target
访问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
来提升代码的性能和灵活性。