C++17默认模板实参类的模板推导:省略空尖括号合法性及推导指南疑问
1. 直接写C c;是否符合C++17标准?
先给明确结论:Clang 6.0和Visual Studio 15.7的行为是符合标准的,GCC 8.0的写法属于编译器扩展。
C++17引入的类模板实参推导(CTAD)核心逻辑是从对象的初始化器中推导模板参数——当你用默认构造函数初始化(也就是写C c;没有任何初始化值)时,没有任何信息能让编译器推导模板参数T。哪怕模板参数有默认实参,CTAD规则也不会自动“兜底”使用默认值。
按照标准,这种场景下你必须显式写出空尖括号C<> c;,明确告诉编译器:“我要使用模板的默认实参来实例化”。这个写法在C++17里保留了和之前版本一致的行为,是标准要求的合法写法。
GCC 8.0允许C c;是它自己实现的非标准扩展:当类模板的所有模板参数都有默认值时,自动用默认值完成实例化。但这不是C++17标准规定的行为,换其他符合标准的编译器就会报错。
2. 针对默认构造函数的推导指南语法是否正确?
你写出的推导指南:
C() -> C<char>;
语法是完全正确的,符合C++17标准中推导指南的定义。
那为什么它在早期编译器中没有生效?因为Clang 6.0和VS 15.7是较早的C++17编译器,对默认构造函数的推导指南支持还不完善。按照标准,这个推导指南会生成一个非模板的候选推导函数C<char> __f();,而类的隐式默认构造函数会生成一个带默认模板参数的模板候选template <class T = char> C<T> __f();。重载解析时非模板函数优先级更高,编译器应该选中推导指南的版本,从而推导出C<char>类型。
不过这个特性在早期编译器实现中没跟上,升级到Clang 10+、Visual Studio 2019+或者GCC 9+这类较新的编译器后,这个推导指南就能正常工作,允许你写出C c;的代码。
3. 实用建议
- 如果要保证代码在所有符合标准的编译器上都能正常编译,别省掉空尖括号,老老实实写
C<> c;。 - 针对默认构造函数的推导指南写法是合法的,但要注意编译器版本支持,只有较新的编译器才能正常识别生效。
内容的提问来源于stack exchange,提问作者fschoenm

