为何C++20 constexpr if仍触发无参数包展开错误?
constexpr if分支仍被编译导致可变模板展开错误
编译错误信息
src/engine/inb/component.h:39:68: error: expected primary-expression before '>' token 39 | swallowTemplateExpansion(i_addComponent<_Components>(inheritor)...); | ^ src/engine/inb/component.h:39:80: error: expansion pattern '((((cosmic::ComponentHolder<_Child>*)this)->cosmic::ComponentHolder<_Child>::i_addComponent < <expression error>) > (inheritor))' contains no parameter packs 39 | swallowTemplateExpansion(i_addComponent<_Components>(inheritor)...); | ^~~
相关代码
ComponentHolder结构体
template<typename _Child> struct ComponentHolder { private: std::vector<std::shared_ptr<Component<_Child>>> components{}; int i_addComponent(_Child* inheritor) { components.push_back(std::make_shared<Component<_Child>>(inheritor)); return 0; } public: template<typename... _Components> constexpr ComponentHolder(_Child* inheritor) { constexpr auto _Size = (sizeof(_Components) + ...); if constexpr (_Size == 0) { return; } else { swallowTemplateExpansion(i_addComponent<_Components>(inheritor)...); } } };
辅助函数
template<typename... T> void swallowTemplateExpansion(T&&... x) {}
问题根源
语法检查优先于constexpr if分支消除:
constexpr if确实会在编译时跳过不执行的分支,但分支内的代码必须先通过语法正确性检查。当_Components参数包为空时,i_addComponent<_Components>的写法语法非法——模板参数列表不能为空,编译器在解析代码时就会报错,不会等到判断分支是否需要执行。错误的参数包大小判断:
你用(sizeof(_Components) + ...)计算的是所有模板类型的sizeof之和,不是参数包的元素个数。更关键的是,空参数包下这种二元折叠表达式本身就是非法的(没有初始值的二元折叠要求参数包非空),正确的参数包个数获取方式是sizeof...(_Components)。错误的函数调用方式:
i_addComponent是普通成员函数,不是模板函数,你却试图用模板参数_Components调用它,这本身就是语法错误,和参数包是否为空无关。
修复方案
步骤1:将i_addComponent改为模板函数
让它接受具体的组件类型作为模板参数,才能针对每个_Components类型创建实例:
template<typename Comp> int i_addComponent(_Child* inheritor) { components.push_back(std::make_shared<Comp>(inheritor)); return 0; }
步骤2:修正参数包判断逻辑(或直接简化)
可以直接用逗号折叠表达式替代constexpr if,空参数包时折叠表达式会自动不执行任何操作:
template<typename... _Components> constexpr ComponentHolder(_Child* inheritor) { // 用逗号折叠逐个调用i_addComponent,空参数包时无操作 (swallowTemplateExpansion(i_addComponent<_Components>(inheritor)), ...); }
如果坚持保留constexpr if,需要用正确的参数包个数判断:
template<typename... _Components> constexpr ComponentHolder(_Child* inheritor) { constexpr auto _Count = sizeof...(_Components); if constexpr (_Count != 0) { swallowTemplateExpansion(i_addComponent<_Components>(inheritor)...); } }
额外说明
空参数包下调用swallowTemplateExpansion()是完全合法的,因此直接使用折叠表达式可以避免constexpr if的语法检查问题,代码更简洁。
内容的提问来源于stack exchange,提问作者user18514542
相关产品推荐
相关产品推荐

