consteval上下文无法更新std::variant的编译问题求助
问题
我正在编写C++编译期解析器,使用Microsoft Visual Studio Community 2019(版本17.8.3)时遇到编译问题。以下代码在consteval函数中更新std::variant时触发编译错误,但将函数签名改为constexpr即可正常编译。
问题代码
#include <array> #include <variant> using namespace std; enum class TokenErrors { UNINITIALISED, TK_ERROR_SYMBOL, TK_ERROR_PATTERN, TK_EOF }; enum class MyTokenType { TK_SYMBOL }; template <typename TokenType, int num_states> struct Lexer { std::array<TokenType, num_states> final_states; }; static consteval auto foo() { auto lxr = Lexer<std::variant<MyTokenType, TokenErrors>, 5>{}; for (auto &x: lxr.final_states) x = TokenErrors::UNINITIALISED; return lxr; } int main() { auto res = foo(); }
编译错误信息
Build started at 17:22... 1>------ Build started: Project: EfficientCompiler, Configuration: Debug x64 ------ 1>Scanning sources for module dependencies... 1>main.cpp 1>C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'Lexer<std::variant<MyTokenType,TokenErrors>,5>' 1>C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1>Invalid aggregate initialization 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> 'initializing': cannot convert from 'initializer list' to 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> Too many braces around initializer for 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> 'initializing': cannot convert from 'initializer list' to 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> Too many braces around initializer for 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> 'initializing': cannot convert from 'initializer list' to 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> Too many braces around initializer for 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> 'initializing': cannot convert from 'initializer list' to 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> Too many braces around initializer for 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> 'initializing': cannot convert from 'initializer list' to 'MyTokenType' 1> C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,19): 1> Too many braces around initializer for 'MyTokenType' 1>C:\Users\Aakash\source\repos\EfficientCompiler\EfficientCompiler\main.cpp(35,16): error C7595: 'foo': call to immediate function is not a constant expression 1>Done building project "EfficientCompiler.vcxproj" -- FAILED. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== ========== Build completed at 17:22 and took 01.116 seconds ==========
解决方案
这个问题源于VS2019对C++20 consteval特性的实现缺陷,在处理std::variant的默认初始化后赋值时存在兼容性问题。可以通过以下几种方式解决:
- 显式构造
std::variant赋值:修改循环中的赋值语句,显式构造std::variant对象,避免隐式转换的问题:
static consteval auto foo() { auto lxr = Lexer<std::variant<MyTokenType, TokenErrors>, 5>{}; for (auto &x: lxr.final_states) x = std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED}; return lxr; }
- 直接初始化数组:在创建
Lexer对象时,直接初始化final_states数组,跳过后续赋值步骤,完全在编译期完成初始化:
static consteval auto foo() { return Lexer<std::variant<MyTokenType, TokenErrors>, 5>{ .final_states = { std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED}, std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED}, std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED}, std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED}, std::variant<MyTokenType, TokenErrors>{TokenErrors::UNINITIALISED} } }; }
- 升级编译器版本:考虑升级到Visual Studio 2022,新版本对C++20特性的支持更完善,已经修复了这类
consteval下的std::variant处理问题。
内容的提问来源于stack exchange,提问作者Aakash Gupta
相关产品推荐
相关产品推荐

