如何将含提前返回逻辑的函数转换为支持任意类型列表的函数模板并完全保留原语义
如何将含提前返回逻辑的函数转换为支持任意类型列表的函数模板并完全保留原语义
这个问题我太理解了——你想要的是完全复刻原函数的核心语义:直接返回第一个有值的foo<T>结果,全程不涉及默认构造后赋值的操作,毕竟自定义类型的默认构造和赋值可能有特殊语义或者额外开销,对吧?
先说说你当前实现的问题:你在call_them里先默认构造了一个std::optional<int> o,之后通过call_one去赋值,这确实和原逻辑不一样——原代码里每一步都是直接返回foo<T>生成的对象,完全没用到默认构造,这就导致了语义上的细微差异。
下面给你两种完全匹配原语义的解决方案:
方案一:递归模板(通用无依赖)
这是最直观、最通用的实现,完全模拟原函数的依次判断逻辑,不依赖任何运算符重载:
// 你的原函数模板和特化保持不变 template <typename> std::optional<int> foo(); template <> std::optional<int> foo<char>() { return {}; } template <> std::optional<int> foo<short>() { return 1; } template <> std::optional<int> foo<int>() { return 2; } // 你的类型列表包装类保持不变 template <typename ...TYPES> struct Types {}; // 递归实现核心:基础情况(单个类型) template <typename T> std::optional<int> call_them_impl() { return foo<T>(); } // 递归实现核心:多类型情况 template <typename T, typename... Rest> std::optional<int> call_them_impl() { if (auto o = foo<T>(); o) { return o; // 直接返回foo<T>生成的对象,和原代码语义完全一致 } return call_them_impl<Rest...>(); } // 对外暴露的接口 template <typename... TYPES> std::optional<int> call_them(Types<TYPES...>) { static_assert(sizeof...(TYPES) > 0, "类型列表不能为空"); return call_them_impl<TYPES...>(); }
逻辑说明:
- 当类型列表只有一个元素时,直接返回对应
foo<T>的结果,和原函数最后一步逻辑一致。 - 当类型列表有多个元素时,先调用第一个类型的
foo<T>,如果有值就直接返回该对象(完全复刻原代码的return o),否则递归处理剩下的类型。 - 全程没有默认构造的中间对象,每一步返回的都是
foo<T>直接生成的对象,完美匹配原语义。
方案二:折叠表达式+C++17特性(简洁高效)
如果你使用的是C++17及以上版本,而且你的自定义类支持类似std::optional的operator||(短路求值,返回第一个有值的对象),那可以用更简洁的写法:
// 原函数模板、特化和Types类保持不变 template <typename... TYPES> std::optional<int> call_them(Types<TYPES...>) { static_assert(sizeof...(TYPES) > 0, "类型列表不能为空"); // 利用折叠表达式的短路特性,依次调用foo<TYPES>(),返回第一个有值的结果 return (foo<TYPES>() || ...); }
逻辑说明:
(foo<TYPES>() || ...)是C++17的折叠表达式,会按照从左到右的顺序依次调用每个foo<TYPES>()。- 由于
std::optional的operator||是短路求值的:如果前面的foo返回有值的optional,就直接返回该对象,后续的foo不会被调用,完全复刻原函数的提前返回逻辑。 - 同样,全程没有默认构造的中间对象,返回的都是
foo<T>直接生成的对象,语义和原函数完全一致。
这两种方案都能完美满足你的需求,你可以根据自己的C++版本和自定义类的特性选择合适的实现。
备注:内容来源于stack exchange,提问作者geza
相关产品推荐
相关产品推荐

