关于std::array的lambda包展开初始化的三个技术疑问
运行时基于索引实例化std::variant的代码疑问解答
先给出这段可在C++17、GCC 8.5环境下正常运行的代码:
template <typename... Ts> std::variant<Ts...> make_variant(std::size_t i) { assert(i < sizeof...(Ts)); static constexpr auto table = std::array{ +[](){ return std::variant<Ts...>{Ts{ }}; }... }; return table[i](); }
针对代码中table数组初始化的疑问,解答如下:
1. 为何此处std::array未指定模板类型?
C++17引入了类模板实参推导(CTAD),编译器可以根据初始化列表的内容自动推导std::array的模板参数。这里初始化列表里的每个元素都是相同类型的函数指针(经过+转换后的lambda指针),编译器能自动推导出std::array的元素类型和大小,因此不需要显式指定std::array<FuncPtr, sizeof...(Ts)>。
2. lambda表达式前的+有何作用?
无捕获的lambda表达式可以隐式转换为对应的函数指针,但lambda本身是匿名闭包类型,直接放入std::array初始化列表时,编译器可能无法自动完成转换。加上+运算符后,会触发lambda到函数指针的隐式转换,将其转换成std::variant<Ts...>(*)()类型的函数指针,确保所有元素类型一致,能被std::array正确容纳。
3. 该折叠表达式的工作原理是什么?
这里的...是C++17的折叠表达式,用于将模板参数包Ts...展开为多个元素。具体来说,+[](){ return std::variant<Ts...>{Ts{ }}; }...会被展开为:
+[](){ return std::variant<Ts1, Ts2, ..., Tsn>{Ts1{ }}; }, +[](){ return std::variant<Ts1, Ts2, ..., Tsn>{Ts2{ }}; }, ... +[](){ return std::variant<Ts1, Ts2, ..., Tsn>{Tsn{ }}; }
每个展开后的lambda函数负责创建对应类型的std::variant实例,这些函数指针最终被初始化到std::array中,运行时通过索引调用对应函数就能得到目标类型的variant。
内容的提问来源于stack exchange,提问作者Caduchon
相关产品推荐
相关产品推荐

