将std::tuple、make_tuple、apply适配至etl库的技术咨询
ETL库适配std::tuple/std::apply的C++模板代码问题
问题梳理
- 误将
etl::variant当作std::tuple的替代方案:ETL中找不到对应std::make_tuple的接口,错误选用etl::variant存储多参数,且初始化方式存疑。 - 索引序列实现apply功能报错:将
std::tuple替换为etl::variant后,自定义的apply逻辑无法编译通过。
原代码(标准库版本)
#include <functional> #include <tuple> template <typename Function, typename... Args> class Class { private: Function function_; std::tuple<Args...> args; public: Class(Function _function, Args... _args) : function_{std::forward<Function>(_function)} , args{std::forward<Args>(_args)...} {} auto function() { return std::apply(function_, args); } };
错误替换尝试代码
// 错误:用variant替代tuple etl::variant<Args...> args; public: Class(Function _function, Args... _args) : function_ { etl::forward<Function>(_function) } , args{/*std::make_tuple*/ etl::forward<Args>(_args)... } // 疑问:是否需要make_tuple? {}
报错的apply适配代码
template <typename... Args, int... Is> void func(etl::variant < Args... > & tup, helper::index < Is... > ) { f(std::get < Is > (tup)...); } template < typename...Args, int...Is > void func(etl::variant < Args... > & tup, helper::index < Is... > ) { f(std::get < Is > (tup)...); } template < typename...Args > void func(etl::variant < Args... > & tup) { func(tup, helper::gen_seq < sizeof...(Args) > {}); // 此处报错 }
解决方案
核心错误纠正
etl::variant是单值变体类型(同一时刻仅能存储一个类型的值),和std::tuple(多值聚合类型,同时存储多个不同类型的值)语义完全不同,不能互相替换。ETL中对应std::tuple的类型是etl::tuple,提供与标准库tuple一致的接口(包括etl::get、etl::make_tuple等)。
修正后的完整代码
#include <etl/delegate.h> #include <etl/tuple.h> #include <etl/utility.h> // 提供etl::forward template <typename Function, typename... Args> class Class { private: // 用etl::delegate替代原始Function类型,更符合ETL库的用法 using FuncSignature = typename etl::function_traits<Function>::signature; etl::delegate<FuncSignature> function_; etl::tuple<Args...> args; public: Class(Function _function, Args... _args) : function_(etl::forward<Function>(_function)) , args(etl::forward<Args>(_args)...) {} auto function() { return apply(function_, args); } private: // 辅助索引序列实现 template <std::size_t... Is> struct index_sequence {}; template <std::size_t N, std::size_t... Is> struct gen_index_sequence : gen_index_sequence<N-1, N-1, Is...> {}; template <std::size_t... Is> struct gen_index_sequence<0, Is...> : index_sequence<Is...> {}; // 适配etl::tuple的apply核心逻辑 template <typename Func, typename... TupleArgs, std::size_t... Is> auto apply_impl(Func&& func, etl::tuple<TupleArgs...>& tup, index_sequence<Is...>) { return etl::forward<Func>(func)(etl::get<Is>(tup)...); } template <typename Func, typename... TupleArgs> auto apply(Func&& func, etl::tuple<TupleArgs...>& tup) { return apply_impl(etl::forward<Func>(func), tup, gen_index_sequence<sizeof...(TupleArgs)>{}); } };
关键说明
- 类型替换:用
etl::tuple直接替换std::tuple,初始化方式与原代码一致,无需额外修改;若需要显式构造,可使用etl::make_tuple。 - 函数封装:用
etl::delegate替代原始函数类型,通过etl::function_traits推导函数签名,更符合ETL库的使用习惯。 - apply实现:ETL部分版本未内置
etl::apply,通过自定义索引序列展开etl::tuple的元素,传递给目标函数,逻辑与std::apply一致。 - 报错原因:原代码中用
etl::variant替代tuple后,std::get<Is>(tup)语义错误——variant的get是获取当前激活的单值,而tuple的get是按索引取多值中的对应元素,二者无法兼容。
内容的提问来源于stack exchange,提问作者Dima S
相关产品推荐
相关产品推荐

