跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
C++11 完美转发
”︁(章节)
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
= C++11完美转发 = '''完美转发'''(Perfect Forwarding)是C++11引入的核心特性之一,它解决了模板函数中参数转发时丢失值类别(value category)的问题,使得参数能够以原始的值类别(左值或右值)被传递到目标函数。 == 问题背景 == 在C++模板编程中,当我们希望编写一个'''泛型转发函数'''时,会遇到参数值类别丢失的问题: <syntaxhighlight lang="cpp"> template<typename T> void forwarder(T arg) { target_function(arg); // 无论传入什么,arg总是左值 } </syntaxhighlight> 传统解决方案存在以下缺陷: * 若使用'''T&'''无法接收右值 * 若使用'''const T&'''会丧失修改能力 * 若为左右值分别重载会导致代码膨胀 == 核心机制 == 完美转发通过以下两个特性协同工作: # '''右值引用'''(T&&) # '''引用折叠规则''': {{!}}- {{!}} 模板参数类型 {{!!}} 传递参数类型 {{!!}} 最终类型 {{!}}- {{!}} T& {{!!}} int& {{!!}} int& {{!}}- {{!}} T& {{!!}} int&& {{!!}} int& {{!}}- {{!}} T&& {{!!}} int& {{!!}} int& {{!}}- {{!}} T&& {{!!}} int&& {{!!}} int&& == std::forward 原理 == 标准库提供的<code>std::forward</code>实现如下: <syntaxhighlight lang="cpp"> template<typename T> T&& forward(typename std::remove_reference<T>::type& arg) noexcept { return static_cast<T&&>(arg); } </syntaxhighlight> 其行为取决于模板参数'''T''': * 当'''T'''为左值引用时(如<code>int&</code>),返回左值引用 * 当'''T'''为非引用或右值引用时,返回右值引用 == 基本用法 == 典型完美转发模式: <syntaxhighlight lang="cpp"> template<typename... Args> void wrapper(Args&&... args) { target(std::forward<Args>(args)...); } </syntaxhighlight> 示例分析: <syntaxhighlight lang="cpp"> #include <iostream> #include <utility> void process(int& x) { std::cout << "左值: " << x << "\n"; } void process(int&& x) { std::cout << "右值: " << x << "\n"; } template<typename T> void forwarder(T&& arg) { process(std::forward<T>(arg)); // 完美转发 } int main() { int a = 10; forwarder(a); // 输出"左值: 10" forwarder(20); // 输出"右值: 20" forwarder(a + 5); // 输出"右值: 15" } </syntaxhighlight> 输出: <pre> 左值: 10 右值: 20 右值: 15 </pre> == 参数包转发 == 可变参数模板中的完美转发: <syntaxhighlight lang="cpp"> template<typename... Args> void emplace_wrapper(Args&&... args) { container.emplace(std::forward<Args>(args)...); } </syntaxhighlight> == 实际应用案例 == === 工厂函数 === <syntaxhighlight lang="cpp"> template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } </syntaxhighlight> === 标准库应用 === STL容器如<code>std::vector::emplace_back</code>的实现: <syntaxhighlight lang="cpp"> template<typename... Args> void emplace_back(Args&&... args) { // 在容器内存直接构造元素 allocator_traits::construct( allocator, end_ptr, std::forward<Args>(args)... ); ++end_ptr; } </syntaxhighlight> == 常见误区 == 1. '''错误使用const限定符''': <syntaxhighlight lang="cpp"> template<typename T> void wrong_forwarder(const T&& arg) { // 错误!会阻止左值绑定 target(std::forward<T>(arg)); } </syntaxhighlight> 2. '''忽略返回值转发''': <syntaxhighlight lang="cpp"> template<typename F, typename... Args> auto call(F&& f, Args&&... args) { return std::forward<F>(f)(std::forward<Args>(args)...); } </syntaxhighlight> == 性能分析 == 完美转发在以下场景带来显著优势: * 避免不必要的拷贝(相比值传递) * 避免对象切片(相比多态基类引用) * 保持移动语义有效性 <mermaid> flowchart LR A[调用方] -->|传递左值/右值| B[转发函数] B -->|完美保持值类别| C[目标函数] C --> D[最优处理] </mermaid> == 进阶主题 == === 完美转发与SFINAE === 结合<code>std::enable_if</code>的条件转发: <syntaxhighlight lang="cpp"> template<typename T, typename = std::enable_if_t<!std::is_array_v<T>>> void smart_forwarder(T&& arg) { process(std::forward<T>(arg)); } </syntaxhighlight> === 转发引用与auto === <code>auto&&</code>同样适用完美转发规则: <syntaxhighlight lang="cpp"> auto&& universal_ref = get_value(); // 自动推导为适当引用类型 process(std::forward<decltype(universal_ref)>(universal_ref)); </syntaxhighlight> == 数学表达 == 完美转发可形式化为类型系统变换: <math> \text{forward} : T \times \text{ValueCategory} \rightarrow \text{PreservedCall} </math> 其中: <math> \text{PreservedCall} = \begin{cases} \text{LValueCall} & \text{if } T \text{ is LValue reference} \\ \text{RValueCall} & \text{otherwise} \end{cases} </math> {{Warning|完美转发会延长临时对象生命周期到完整表达式结束,但不会超过转发函数调用范围}} == 练习建议 == 1. 实现一个通用日志包装器,完美转发参数到输出流 2. 编写类型特征检查工具,验证转发是否保持值类别 3. 对比分析完美转发与常规传参的性能差异 [[Category:编程语言]] [[Category:C++]] [[Category:C++11 新特性]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)