You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模板递归调用与折叠表达式为何在类型转换时存在输出差异?

问题:C++递归打印函数与折叠表达式打印函数输出不一致的原因

原代码

#include <array>
#include <type_traits>
#include <iostream>

template<class... Types>
class Test {
 public:
  Test()
    : format_({
      [] {
      if constexpr(std::is_same_v<Types, int64_t>) {
        return "%ld ";
      } else if constexpr(std::is_same_v<Types, int32_t>) {
        return "%d ";
      } else if constexpr(std::is_same_v<Types, double>) {
        return "%f ";
      }
      }() ...
    }) {}

  template<typename T, typename ... Args>
  void print_recursive(T && v, Args && ... args) {
    size_t i = sizeof...(Types) - sizeof...(args) - 1;
    printf(format_[i], std::forward<T>(v));
    if constexpr(sizeof...(Args) > 0) {
      print_recursive(std::forward<Args>(args)...);
    } else {
      printf("\n");
    }
  }

  void print_fold(Types && ... args) {
    size_t i = 0;
    ([&]{
      printf(format_[i++], args);
    }(), ...);
    printf("\n");
  }

 private:
  std::array<const char*, sizeof...(Types)> format_;
};

int main() {
  Test<int64_t, int32_t, double> t;
  t.print_recursive(1.0, 2, 3.0);
  t.print_fold(1.0, 2, 3.0);
}

输出结果

0 2 3.000000 
1 2 3.000000 

编译运行命令

g++ test.cpp -o test -std=c++17 && ./test

编译器信息

$ gcc --version
gcc (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

问题描述

上述代码中,format_数组通过折叠表达式初始化,两个打印函数使用的格式说明符对应索引一致,传入的参数也相同,但输出结果却不一致,这是什么原因?

原因分析

1. print_fold的参数处理

print_fold的参数是Types&&... args,与类模板参数int64_t, int32_t, double严格匹配。调用t.print_fold(1.0, 2, 3.0)时:

  • 第一个参数1.0(double类型)会被隐式转换为int64_t,值为1;
  • 第二个参数2(int类型)转换为int32_t,值为2;
  • 第三个参数3.0保持double类型不变。
    打印时每个参数都对应正确的格式说明符,因此输出正确的1 2 3.000000。

2. print_recursive的参数处理

print_recursive是模板函数,参数类型由推导而来,不会自动转换为类模板的Types类型:

  • 第一次调用t.print_recursive(1.0, 2, 3.0)时,T推导为double,Args推导为int, double。计算索引i = 3 - 2 - 1 = 0,对应format_[0]是为int64_t设计的%ld,但实际传入的是double类型的1.0,用%ld打印double属于未定义行为,当前编译器下输出0;
  • 第二次递归调用print_recursive(2, 3.0),T推导为int,Args推导为double,索引i = 3 -1 -1 =1,对应format_[1]的%d,参数是int类型的2,输出正确;
  • 第三次递归调用print_recursive(3.0),T推导为double,Args为空,索引i=3-0-1=2,对应format_[2]的%f,参数是3.0,输出正确。
    最终得到错误的0 2 3.000000。

修正方案

新增入口函数,先将参数转换为类模板的Types类型,再传入递归实现函数,确保参数类型与格式说明符匹配:

void print_recursive(Types && ... args) {
  print_recursive_impl(std::forward<Types>(args)...);
}

template<typename T, typename ... Args>
void print_recursive_impl(T && v, Args && ... args) {
  size_t i = sizeof...(Types) - sizeof...(args) - 1;
  printf(format_[i], std::forward<T>(v));
  if constexpr(sizeof...(Args) > 0) {
    print_recursive_impl(std::forward<Args>(args)...);
  } else {
    printf("\n");
  }
}

该方案中,入口函数强制将传入参数转换为Types对应类型,递归时参数类型与格式说明符完全匹配,即可得到正确输出。


内容的提问来源于stack exchange,提问作者ajoseps

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 02:35:19