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

如何将std::format的格式信息转发给自定义类型的formatter?

在MSVC中为自定义类型实现带格式转发的std::format支持

你的需求中,直接用std::format("{:x}{:*^10}", person)会触发编译错误——因为std::format要求占位符数量必须和传入的参数数量严格匹配,这里2个占位符对应1个Person参数,不符合语法规则。

要实现类似“用单个Person参数对应多个格式占位符”的效果,可以通过以下两种方式解决:


方式一:自定义formatter解析复合格式说明

让std::formatter<Person>支持解析复合格式字符串,通过分隔符(比如分号)区分两个成员的格式规则,用单个占位符完成多成员的格式化:

#include <format>
#include <iostream>
#include <string>
#include <stdexcept>

class Person {
public:
    int age;
    std::string name;
};

template <>
struct std::formatter<Person> {
    std::string age_fmt;
    std::string name_fmt;

    // 解析格式说明,例如 "{:x;*^10}" 会拆分为age_fmt="x",name_fmt="*^10"
    constexpr auto parse(std::format_parse_context& ctx) {
        auto it = ctx.begin();
        const auto end = ctx.end();

        // 读取age的格式规则,直到遇到分号或格式结束符
        while (it != end && *it != ';' && *it != '}') {
            age_fmt += *it++;
        }

        // 如果存在分号,继续读取name的格式规则
        if (it != end && *it == ';') {
            ++it;
            while (it != end && *it != '}') {
                name_fmt += *it++;
            }
        }

        // 格式串必须以 } 结尾,否则抛出错误
        if (it != end && *it != '}') {
            throw std::format_error("invalid Person format specifier");
        }

        return it;
    }

    template <typename FormatContext>
    auto format(const Person& p, FormatContext& ctx) {
        // 根据解析出的格式规则,分别格式化age和name
        if (age_fmt.empty() && name_fmt.empty()) {
            return std::format_to(ctx.out(), "{} {}", p.age, p.name);
        } else if (name_fmt.empty()) {
            return std::format_to(ctx.out(), "{:" + age_fmt + "} {}", p.age, p.name);
        } else if (age_fmt.empty()) {
            return std::format_to(ctx.out(), "{} {:" + name_fmt + "}", p.age, p.name);
        } else {
            return std::format_to(ctx.out(), "{:" + age_fmt + "} {:" + name_fmt + "}", p.age, p.name);
        }
    }
};

int main() {
    Person person{30, "John Doe"};
    // 使用复合格式说明,分号分隔age和name的格式
    std::cout << std::format("{:x;*^10}", person) << '\n';
    // 输出结果:1e ****John Doe****
    return 0;
}

方式二:自定义格式化函数

如果想直接复用原格式串"{:x}{:*^10}",可以编写一个封装函数,自动将Person的成员作为参数传入std::format:

#include <format>
#include <iostream>
#include <string>
#include <string_view>

class Person {
public:
    int age;
    std::string name;
};

// 自定义格式化函数,自动拆分Person的成员
template <typename... Args>
std::string format_person(std::string_view fmt, const Person& p) {
    return std::format(fmt, p.age, p.name);
}

int main() {
    Person person{30, "John Doe"};
    // 直接使用原格式串调用自定义函数
    std::cout << format_person("{:x}{:*^10}", person) << '\n';
    // 输出结果:1e****John Doe****
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:57:29