嵌套花括号initializer_list在GCC、MSVC中存储失败,仅Clang支持
跨编译器兼容的std::initializer_list嵌套初始化问题
问题描述
需要临时存储std::initializer_list<topic>用于处理,允许通过嵌套花括号向register_topics函数传入结构化的topic对象。每个topic对象会将初始化列表存储至完整表达式结束,供register_topics访问并将主题存入vector等容器。这段代码在Clang 10及以上版本可正常编译,但在GCC 12和MSVC中编译失败,请问是代码问题还是编译器问题?如何修改实现跨主流编译器兼容?
代码示例
#include <cstdio> #include <initializer_list> struct topic { using callback_type = void (*)(); topic() = default; topic(const char* name, callback_type fn, std::initializer_list<topic> init = {}) : name_{name}, fn_{fn}, init_{init} { } const char* name_; callback_type fn_; std::initializer_list<topic> init_; }; void register_topics(topic topic_structure) { /* 在此处处理topic_structure,需访问topic对象临时存储的initializer_list */ } int main() { auto lambda1 = []() -> void {}; register_topics({ "hello", lambda1, { {"hello2", lambda1}, { "hello3", lambda1 }} }); }
GCC 12编译错误
<source>: In constructor 'topic::topic(const char*, callback_type, std::initializer_list<topic>)': <source>:11:13: error: could not convert '{init}' from '<brace-enclosed initializer list>' to 'std::initializer_list<topic>' 11 | init_{init} | ^~~~~~~~~~~ | | | <brace-enclosed initializer list>
原因分析
这是代码写法不符合C标准导致的兼容性问题。问题出在构造函数的成员初始化列表中:用init_{init}这种花括号初始化方式赋值std::initializer_list<topic>类型的成员。虽然Clang对这种写法做了扩展支持,但GCC和MSVC严格遵循C标准——std::initializer_list的构造不支持将另一个std::initializer_list对象放在花括号里进行隐式转换。
修改方案
将构造函数成员初始化列表中的init_{init}改为init_(init),直接使用括号初始化而非花括号初始化:
修改后的完整代码
#include <cstdio> #include <initializer_list> struct topic { using callback_type = void (*)(); topic() = default; topic(const char* name, callback_type fn, std::initializer_list<topic> init = {}) : name_{name}, fn_{fn}, init_(init) // 替换花括号为括号,符合标准初始化方式 { } const char* name_; callback_type fn_; std::initializer_list<topic> init_; }; void register_topics(topic topic_structure) { /* 在此处处理topic_structure,需访问topic对象临时存储的initializer_list */ } int main() { auto lambda1 = []() -> void {}; register_topics({ "hello", lambda1, { {"hello2", lambda1}, { "hello3", lambda1 }} }); }
额外说明
std::initializer_list的底层数组是临时对象,其生命周期仅持续到完整表达式结束。当前代码中register_topics按值接收topic参数,topic对象的init_指向的临时数组会在register_topics调用完成前保持有效,完全符合需求,无需额外修改。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

