C++17中GCC报“参数包未用...展开”错误,是否为编译器Bug?
GCC编译失败:参数包未展开错误的原因与修复方案
问题本质:GCC的模板依赖类型解析Bug
这是GCC 9-11版本的编译器Bug,而非C++语法使用不当。问题出在:类模板myclass中的别名payload_type依赖于类的模板参数包types...,当在成员函数模板try_push的返回值或变量声明中直接使用std::optional<payload_type>时,GCC未能正确识别payload_type是依赖外部模板参数的类型,错误判定此处存在未展开的参数包。
而decltype(t)能正常编译,是因为decltype会触发类型推导,编译器能正确解析t的实际类型(即payload_type),绕开了依赖类型的解析问题。
修复方案
以下三种方式均可让代码在GCC上正常编译:
方案1:显式添加typename标注
直接告诉编译器payload_type是依赖类型:
// 修改返回值类型 std::pair<bool, std::optional<typename myclass<types...>::payload_type>> try_push(T &&...data) // 修改变量声明 std::optional<typename myclass<types...>::payload_type> res;
方案2:用decltype替代直接使用别名
利用decltype的类型推导特性,避免直接写payload_type:
// 返回值类型写法 std::pair<bool, std::optional<decltype(std::declval<payload_type>())>> try_push(T &&...data) // 变量声明沿用可行写法 payload_type t; std::optional<decltype(t)> res;
方案3:将别名包装为内部模板
把payload_type的定义改成内部模板,帮助GCC正确解析依赖关系:
template<typename... types> class myclass { template<typename... Ts> using payload_type_t = type_or_tuple_t<Ts...>; using payload_type = payload_type_t<types...>; template<typename... T> std::pair<bool, std::optional<payload_type>> try_push(T &&...data) { std::optional<payload_type> res; // ... 其他逻辑 return {true, std::move(res)}; } };
完整修复示例
以下是采用方案1的可编译完整代码:
#include <iostream> #include <type_traits> #include <optional> #include <tuple> using namespace std; template<typename... types> struct type_or_tuple { using tup = std::tuple<types...>; using T = std::conditional_t<(std::tuple_size_v<tup> == 1), std::tuple_element_t<0, tup>, tup>; using is_tuple = std::conditional_t<(std::tuple_size_v<tup> > 1), std::true_type, std::false_type>; }; template<typename... types> using type_or_tuple_t = typename type_or_tuple<types...>::T; template<typename ...types> class myclass { using payload_type = type_or_tuple_t<types...>; template<typename... T> std::pair<bool, std::optional<typename myclass<types...>::payload_type>> try_push(T &&...data) { payload_type t; std::optional<decltype(t)> o; std::optional<typename myclass<types...>::payload_type> res; // 现在可正常编译 return {true, std::move(o)}; } }; int main(int, const char **){ myclass<unsigned> c; }
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

