递归可变参数函数模板疑问:第二种实现为何允许空参数调用?
第二种print实现的递归合法性解析
先把两种实现和测试代码整理如下:
第一种实现
void print() {} template<typename T, typename... Ts> void print(T first, Ts... args) { std::cout << first << '\n'; print(args...); }
第二种实现
template<typename T> void print(T t) { std::cout << t << '\n'; } template<typename T, typename... Ts> void print(T first, Ts... args) { print(first); print(args...); }
测试代码
int main() { using namespace std::literals::string_literals; print("hello"s, 22, "world"s); }
问题核心与解答
你疑惑的点其实存在一个关键误解:你的测试用例的递归流程根本不会触发无参print()的调用,自然也就不存在合法性问题。我们拆解具体调用流程就能明白:
- 初始调用
print("hello"s, 22, "world"s)会匹配可变参数模板(第二个函数):T推导为std::string,Ts为int, std::string。函数内部先调用print("hello"s),这会匹配单参数模板(第一个函数)并输出内容;接着调用print(22, "world"s)。 print(22, "world"s)再次匹配可变参数模板:T推导为int,Ts为std::string。内部先调用print(22)匹配单参数模板并输出;接着调用print("world"s)。print("world"s)直接匹配单参数模板,输出后递归结束——全程没有任何环节会调用无参的print()。
第二种实现的递归终止条件是单参数模板,和第一种依赖无参函数终止递归的逻辑完全不同:它每次先处理第一个参数,再递归处理剩余参数,最后剩下的单个参数会直接匹配单参数模板,递归到此自然结束。
另外补充一点模板重载规则:当存在两个可匹配的模板(比如单参数模板,以及Ts...为空时的可变参数模板),编译器会选择更特化的版本——也就是单参数模板,这进一步保证了最后一步的调用不会去尝试寻找无参函数。
内容的提问来源于stack exchange,提问作者eduard
相关产品推荐
相关产品推荐

