C++17中折叠表达式与Lambda的结合使用
C++17中折叠表达式与Lambda的结合使用
嘿,我完全get到你的需求了——你有个基于可变参数模板继承的派生类,各个基类和派生类都有个签名一模一样的方法,返回的是对象指针;现在你想借助C++17的折叠表达式,遍历调用每个基类的这个方法,还得精准拿到某个指定基类的方法返回值,如果参数包里压根没这个基类,就返回nullptr对吧?这事儿用折叠表达式配合Lambda就能轻松搞定,我给你一步步拆解。
首先咱们先搭个基础的代码场景,模拟你遇到的情况:
#include <type_traits> // 几个示例基类,都有foo方法返回自身指针 struct BaseA { BaseA* foo() { return this; } }; struct BaseB { BaseB* foo() { return this; } }; struct BaseC { BaseC* foo() { return this; } };
接下来是你的派生类,继承自可变参数模板里的所有基类。核心就是在这个派生类里实现一个模板方法,用折叠表达式完成咱们要的逻辑:
template <typename... Bases> struct Derived : Bases... { // 模板方法:传入目标基类类型,返回它的foo方法结果,没有则返回nullptr template <typename TargetBase> TargetBase* get_target_foo() { TargetBase* result = nullptr; // 默认返回nullptr // 用折叠表达式展开所有基类,每个基类对应执行一次Lambda ( [&]() { // 编译期判断当前基类是否是我们要找的目标类型 if constexpr (std::is_same_v<Bases, TargetBase>) { // 调用该基类的foo方法,转成目标类型指针赋值给result result = static_cast<TargetBase*>(Bases::foo()); } }(), ... ); return result; } };
我给你解释下这段代码的关键细节:
- 我们先初始化
result为nullptr,作为找不到目标基类时的默认返回值。 - 折叠表达式
( [&](){...}(), ... )会把参数包Bases...里的每个基类都展开一遍,每个基类对应执行一次Lambda表达式。 if constexpr是C++17的编译期分支特性,它会在编译阶段就判断当前基类是不是我们要找的TargetBase,只有匹配的情况才会生成对应的代码,完全不会有运行时的额外开销,效率拉满。- 这里用
static_cast<TargetBase*>是因为Bases::foo()返回的是Bases*,当Bases和TargetBase是同一类型时,这个转换是绝对安全的,能保证返回指针的类型正确。
咱们再写个测试代码验证下效果:
int main() { Derived<BaseA, BaseC> d; // 找BaseA的foo返回值,能拿到有效指针 BaseA* a_ptr = d.get_target_foo<BaseA>(); // 找BaseB的foo,因为派生类没继承BaseB,返回nullptr BaseB* b_ptr = d.get_target_foo<BaseB>(); // 找BaseC的foo,能拿到有效指针 BaseC* c_ptr = d.get_target_foo<BaseC>(); return 0; }
另外还有个小细节:如果你的参数包里不小心重复了同一个基类(比如Derived<BaseA, BaseA>),上面的代码会让最后一个匹配的基类覆盖result的值。如果你想返回第一个匹配的结果,只需要给if constexpr加个额外判断就行:
if constexpr (std::is_same_v<Bases, TargetBase> && result == nullptr) { result = static_cast<TargetBase*>(Bases::foo()); }
这样只要result还是nullptr(也就是还没找到第一个匹配的基类),才会赋值,找到第一个后就不会再处理后续的重复基类了。
总的来说,这种把折叠表达式和Lambda结合的方式,既利用了折叠表达式简洁展开参数包的优势,又通过Lambda捕获变量来收集我们需要的结果,配合编译期的类型判断,完美解决了你遇到的问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

