如何优雅无分支实现适配单数据成员类型的结构化绑定apply函数?
优雅实现针对单数据成员类型的apply1函数
std命名空间中提供了便捷的std::apply函数,可对tuple的每个元素执行操作。借助结构化绑定,我们能为非tuple类型实现类似行为。针对仅含一个非静态数据成员的类型,有人编写了带if constexpr分支的apply1函数:
#include <iostream> #include <functional> #include <utility> #include <type_traits> template<class F, class T> decltype(auto) apply1(F&& func, T&& val){ if constexpr(std::is_lvalue_reference<decltype(val)>()){ auto& [m1] = val; return std::invoke(std::forward<F>(func), m1); } else { auto&& [m1] = std::forward<T>(val); return std::invoke(std::forward<F>(func), std::forward<decltype(m1)>(m1)); } } //tests struct obj{int v;}; struct data{obj o;}; void ref(obj&){std::cout<<"&\n";} void cref(const obj&){std::cout<<"const&\n";} void temp(obj&&){std::cout<<"&&\n";} int main(){ data d{}; apply1(ref, d); apply1(cref, d); //SHOULD NOT COMPILE apply1(temp, d); apply1(temp, std::move(d)); }
这段代码的分支逻辑让整体不够优雅,但移除分支后现有测试用例无法正常运行。那能不能在不使用分支的前提下,更优雅地实现apply1函数,同时保证上述测试用例正常运行?
当然可以,核心是利用完美转发和结构化绑定的引用折叠规则,无需分支就能正确处理所有值类别和const属性的情况。优化后的实现如下:
#include <iostream> #include <functional> #include <utility> #include <type_traits> template<class F, class T> decltype(auto) apply1(F&& func, T&& val){ auto&& [m1] = std::forward<T>(val); return std::invoke(std::forward<F>(func), std::forward<decltype(m1)>(m1)); } // 测试代码 struct obj{int v;}; struct data{obj o;}; void ref(obj&){std::cout<<"&\n";} void cref(const obj&){std::cout<<"const&\n";} void temp(obj&&){std::cout<<"&&\n";} int main(){ data d{}; apply1(ref, d); // 调用ref(obj&),输出& apply1(cref, d); // 调用cref(const obj&),输出const& // apply1(temp, d); // 编译失败,符合预期 apply1(temp, std::move(d)); // 调用temp(obj&&),输出&& }
原理说明
auto&& [m1] = std::forward<T>(val):auto&&会根据val的原始值类别(左值/右值)自动推导合适的引用类型,配合std::forward<T>(val)完美转发val的所有属性(包括const、volatile等限定符),结构化绑定会正确绑定到成员的对应引用类型。std::forward<decltype(m1)>(m1):通过decltype(m1)保留绑定后的成员引用类型,再用std::forward将成员的引用属性完美转发给std::invoke,确保函数参数的匹配规则完全符合预期。
这种写法既消除了冗余的分支逻辑,代码更简洁,同时完全保留了原版本的所有功能:
- 传入左值时,成员绑定为左值引用,可匹配
obj&和const obj&的函数重载 - 传入右值时,成员绑定为右值引用,可匹配
obj&&的函数重载 - 传入const左值时,成员会绑定为const左值引用,自动匹配对应的const重载(可自行补充测试验证)
内容的提问来源于stack exchange,提问作者PavelDev
相关产品推荐
相关产品推荐

