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

基于参数数量推导可变参数模板的format函数实现问题

问题描述

现有两个函数:

void string(const char *str)
{
    std::cout << "this is string" << std::endl;
}

void number(const char *str, double f)
{
    std::cout << "this is number" << std::endl;
}

需要编写通用包装函数format,支持如下调用方式:

int main() {
    format("single arg");
    format("format string", 1.0);
    format("single arg", "format string", 1.0);
    format("format string 1", 1.0, "just string arg", "format string 2", 2.0);
    return 0;
}

核心要求:

  • 参数以「字符串,数字」成对出现时调用number(),否则调用string()
  • 必须从右往左解包参数

尝试的错误实现如下:

template<class T>
void operation(T first)
{
    string(first);
}

template<class T, class U = float>
void operation(T first, U second)
{
    number(first, second);
}

template<class ... ARGS>
void format(ARGS ... args)
{
    auto last = (args, ...);
    using last_type = typename decltype(last);
    if constexpr (std::is_arithmetic_v<last_type>)
        (..., operation(args, last));
    else
        (..., operation(args)); 
}

该实现的问题:解包operation(args, last)时,会将所有args与last配对传入,导致出现两个浮点数作为参数的非法调用,完全不符合需求。

解决方案

可以通过递归模板函数实现从右往左的参数处理逻辑,无需依赖元组,代码简洁且符合编译期类型检查要求:

#include <type_traits>
#include <iostream>

// 原函数保持不变
void string(const char *str)
{
    std::cout << "this is string" << std::endl;
}

void number(const char *str, double f)
{
    std::cout << "this is number" << std::endl;
}

// 递归终止条件:无参数时直接返回
void format() {}

// 处理单个字符串参数(最基础的单参数情况)
template<typename T>
std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>>
format(T str) {
    string(str);
}

// 优先处理末尾的「字符串+数字」配对:先调用number,再递归处理剩余参数
template<typename T, typename U, typename... Rest>
std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*> && std::is_arithmetic_v<std::decay_t<U>>>
format(T str, U num, Rest... rest) {
    number(str, static_cast<double>(num));
    format(rest...);
}

// 处理单个字符串参数后面还有其他参数的情况:先递归处理后面的参数,再调用string
template<typename T, typename... Rest>
std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>>
format(T str, Rest... rest) {
    format(rest...);
    string(str);
}

// 测试用例
int main() {
    format("single arg");
    format("format string", 1.0);
    format("single arg", "format string", 1.0);
    format("format string 1", 1.0, "just string arg", "format string 2", 2.0);
    return 0;
}

关键逻辑说明

  1. 利用std::enable_if和类型萃取做编译期分支判断,确保只有合法的参数组合会触发对应的函数重载
  2. 递归调用时严格遵循从右往左的处理顺序:
    • 若末尾是「字符串+数字」,先处理该配对(调用number),再递归处理前面的剩余参数
    • 若末尾是单个字符串,先递归处理前面的所有参数,最后处理当前字符串(调用string)
  3. 整个实现仅依赖C++17及以上的模板特性,无需额外容器或库支持

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 13:15:34