关于可变参数模板的编译器行为差异:MSVC报C3520/C3543错误的技术咨询
咱们先来明确你遇到的场景,先看你提供的代码:
template<int...n> struct StrStuff { explicit StrStuff(char const(&...s)[n]) {} }; int main() { StrStuff g("apple", "pie"); }
正如你所说,这段代码在GCC和Clang里能正常编译通过,但在x86 MSVC v19.43(VS17.13)里会抛出两个错误:
error C3520: 'n': parameter pack must be expanded in this contexterror C3543: 'const char (&)[0]': does not contain a parameter
而你提到的临时解决方案——把template<int...n>移到构造函数定义前,让构造函数拥有自己的模板参数包,MSVC就能正常编译了。这背后其实是MSVC对C++标准中**类模板参数推导(CTAD)**的实现存在兼容性问题,而非你遗漏了什么语法规则。
核心原因拆解
GCC和Clang的处理更符合C++标准的预期:当类模板的构造函数使用了类模板自身的参数包n时,CTAD过程中编译器应该能自动推导每个参数对应的模板参数(也就是每个字符串字面量的长度,比如"apple"对应5,"pie"对应4),完成参数包的展开。
但MSVC在这里有个额外的限制:它要求类模板的参数包必须在构造函数的参数列表中被“直接显式展开”,但实际上C++标准并没有这个强制要求。直白点说,这是MSVC在类模板参数推导与可变参数包结合场景下的实现缺陷。
你可以做个小验证:如果手动指定类模板的参数,比如写成StrStuff<5,4> g("apple", "pie");,这时候MSVC就能正常编译了——这说明它能处理显式指定的参数包,只是在自动推导(CTAD)环节对构造函数复用类模板参数包的情况支持不足。
结论
你完全没有搞错语法,这确实是MSVC和GCC/Clang之间的兼容性差异,属于MSVC的实现问题。如果需要兼容MSVC,你可以继续使用把参数包移到构造函数模板的方案,或者在实例化时显式指定类模板参数。后续的MSVC版本大概率会修复这个问题,跟进标准的处理逻辑。
内容来源于stack exchange

