C++可变模板递归编译时函数两类编译失败问题咨询
C++可变模板编译失败问题解析
问题背景
首先定义存储多字符的模板类型,以及一个可正常工作的参数包打印函数:
template<char...chars> struct Test{ }; // 参数包打印版本可正常工作 template<char...chars> constexpr std::ostream& toOstream( std::ostream & os , Test <chars...> ) { return ( os << ... << chars ) ; };
下面分析两个编译失败场景及补充问题:
Case A:递归版本打印函数编译失败
递归写法代码:
// Case A template<char c=0, char...chars> constexpr std::ostream& toOstream2( std::ostream & os , Test <c,chars...> ) { return sizeof...(chars) == 0 ? ( os << c ) : toOstream2( os << c, Test<chars...>() ) ; };
报错信息:候选函数需2个参数但未提供。
原因分析:
- 递归调用时,
Test<chars...>无法匹配函数模板的Test<c, chars...>参数——后者要求模板参数至少包含一个字符(c),而当chars为空时,Test<chars...>是Test<>,没有对应的函数重载。 - 三元运算符会强制编译器实例化两个分支,即便
sizeof...(chars)==0,递归分支仍会被尝试实例化,此时找不到匹配的函数模板,触发报错。 - 正确的递归写法需要拆分终止版本和递归版本,让编译器在编译期通过重载解析选择对应分支:
// 递归终止版本:处理单个字符的情况 template<char c> constexpr std::ostream& toOstream2(std::ostream& os, Test<c>) { return os << c; } // 递归版本:处理多个字符的情况 template<char c, char...chars> constexpr std::ostream& toOstream2(std::ostream& os, Test<c, chars...>) { os << c; return toOstream2(os, Test<chars...>()); }
Case B:派生类中使用模板类型报错
派生类代码:
// Case B template< class Base > struct Deriv{ static constexpr std::ostream& toOstream( std::ostream & os ) { return ( os << Base() ); }; };
报错信息:std::ostream与Test<'k','a','b'>无匹配operator<<。
原因分析:std::ostream没有默认的输出运算符支持Test类型对象,之前定义的toOstream是独立函数而非operator<<重载,直接写os << Base()会找不到匹配的操作符。
解决方法:
- 方法一:调用已有的
toOstream函数:
template< class Base > struct Deriv{ static constexpr std::ostream& toOstream( std::ostream & os ) { return ::toOstream(os, Base()); }; };
- 方法二:重载
std::ostream的输出运算符:
template<char...chars> constexpr std::ostream& operator<<(std::ostream& os, Test<chars...>) { return (os << ... << chars); }
补充问题:为何整数递归函数正常,Case A不行?
正常编译的整数递归函数:
template<int First=0, int... Rest> constexpr int fauo() { return sizeof...(Rest)==0 ? First : First + fauo<Rest...>(); } #include<iostream> int main(void){ std::cout << fauo<0,9>() << std::endl; return 0; }
差异原因:
- 整数递归函数是纯非类型模板参数的递归,递归调用
fauo<Rest...>()时,若Rest为空,会匹配带默认参数First=0的函数模板,不存在参数不匹配问题。 - Case A的递归依赖函数参数(
Test类型对象)的推导,原写法没有设计对应的空参数包重载,且三元运算符强制实例化无效分支,导致推导失败。 - 两者核心差异在于:整数递归仅涉及模板参数的展开与默认值补全,而Case A涉及函数参数的模板推导与重载解析,规则逻辑完全不同。
内容的提问来源于stack exchange,提问作者ctejeda
相关产品推荐
相关产品推荐

