GCC 10.1及以下自定义类推导指南失效,std::tuple却正常的问题
自定义模板类推导指南在低版本GCC失效的原因分析
问题场景
自定义模板类Foo的推导指南在GCC 10.1及以下版本无法生效,执行Foo mp {1, 3, 5};时,输出的Foo template pack is为0;但新版GCC中可正常输出3。同时,同样在GCC 10.1及以下版本中,std::tuple却能正确推导类型(如std::tuple m{1,2,4}会被推导为std::tuple<int,int,int>)。
代码示例:
#include <iostream> template <typename... T> struct Foo { template <typename... U> constexpr Foo(U&&... args) { std::cout << "Foo arg size is " << sizeof...(args) << "\n"; std::cout << "Foo template pack is " << sizeof...(T) << "\n"; } }; template<typename... _UTypes> Foo(_UTypes...) -> Foo<_UTypes...>; int main() { Foo mp {1, 3, 5}; return 0; }
原因分析
1. 低版本GCC的类模板推导优先级bug
根据C++17及后续标准的规定,类模板推导指南的优先级高于类内部的模板构造函数。当编写Foo mp{1,3,5}时,编译器应该优先使用我们提供的推导指南Foo(_UTypes...) -> Foo<_UTypes...>,将类模板参数T推导为int,int,int,从而让sizeof...(T)输出3。
但GCC 10.1及以下版本存在实现缺陷:它错误地优先选择了类内部的模板构造函数来推导类模板参数T。而这个模板构造函数的参数包U和类模板参数T没有直接关联,编译器无法通过U推导出T,最终导致T被推导为空参数包,所以sizeof...(T)输出0。
2. std::tuple能正常工作的原因
std::tuple在低版本GCC中可以正确推导,核心在于它的构造函数设计和推导指南的配合逻辑避开了这个bug:
std::tuple的构造函数并非是完全独立的模板构造函数,而是直接和类模板参数绑定的(或者其内部构造函数的推导逻辑与推导指南完全一致);- 低版本GCC对
std::tuple的推导处理没有触发这个优先级错误,因此能够正确识别推导指南,推导出对应的模板参数。
验证与临时解决
如果把Foo的模板构造函数修改为与类模板参数直接绑定的非模板构造函数:
constexpr Foo(T&&... args) { // ... }
此时低版本GCC也能正确推导T的参数包,因为构造函数直接关联了T的推导逻辑,和推导指南的行为一致。
内容的提问来源于stack exchange,提问作者DDG
相关产品推荐
相关产品推荐

