用户定义类型的std::format?

24
在C++20中,如何使用户自定义类型与std::format兼容?例如,假设我有一个名为Point的类型:
struct Point {
    int x;
    int y;
};

使用其定义了operator<<的:

inline std::ostream&
operator<<(std::ostream& o, Point pt)
{ return o << "[" << pt.x << << ", " << pt.y << "]"; }

下面这个程序会输出Hello [3, 4]!吗?

int main() {
   Point pt{3,4};
   std::cout << std::format("Hello {}!\n", pt);
}

如果是 - 为什么以及如何?

如果不是 - 我需要在 Point 的定义中添加什么才能使其工作?


@rafix07:你确定这个特性被带入了C++20吗? - Andrew Tomazos
你应该重载 std::formatter<Ti, CharT> - unegare
别忘了投票 =)。 - vitaut
2个回答

30

std::format不支持operator<<,你需要为你的类型(Point)提供一个formatter特化。最简单的方法是重用现有的格式化器之一,例如std::formatter<std::string>

template <>
struct std::formatter<Point> : std::formatter<std::string> {
  auto format(Point p, format_context& ctx) const {
    return formatter<string>::format(
      std::format("[{}, {}]", p.x, p.y), ctx);
  }
};

这将为您提供std::string支持的所有格式规范。以下是一个示例,使用'~'填充并居中对齐Point,使其占据10个字符的空间:
auto s = std::format("{:~^10}", Point{1, 2});
// s == "~~[1, 2]~~"

使用iostreams实现这一点并不容易。

4
еңЁC++ж ҮеҮҶеә“зҡ„std::formatдёӯжІЎжңүеҢ…еҗ«operator<<пјҢиҝҷжҳҜеӣ дёәе®ғдјҡеј•е…ҘеҫҲеӨҡй—®йўҳпјҢдҫӢеҰӮиҫ“еҮәеҸ–еҶідәҺformatterе…·дҪ“еҢ–зҡ„еҸҜи§ҒжҖ§пјҢз”ҡиҮіеҸҜиғҪеҜјиҮҙODRиҝқ规гҖӮ - vitaut
请注意,https://en.cppreference.com/w/cpp/utility/format 目前声称“...并重用一些[I/O流]基础设施,例如用户定义类型的重载插入运算符”,这与此答案不一致。 - jwnimmer-tri
cppreference在这种情况下是错误的,std::format有自己的扩展机制,不使用operator<<。 - vitaut
1
请注意,cppreference上关于formatoperator<<的错误陈述已被删除。 - Max Truxa
我假设CE上的“x86-64 gcc(trunk)”已经在最近几天内构建完成,包括libstdc ++。我想知道是否有在线提供libc ++的主干版本构建。 - user2023370
显示剩余3条评论

8

您需要为您的类型专门定制std::formatter

namespace std
{
    template<class CharT>
    struct formatter<Point, CharT>
    {  
        template <typename FormatParseContext>
        auto parse(FormatParseContext& pc)
        {
            // parse formatter args like padding, precision if you support it
            return pc.end(); // returns the iterator to the last parsed character in the format string, in this case we just swallow everything
        }

        template<typename FormatContext>
        auto format(Point p, FormatContext& fc) 
        {
            return std::format_to(fc.out(), "[{}, {}]", p.x, p.y);
        }
    };
}

我不认为流输出运算符能够起作用,但我没有任何来源来支持这一说法。


标准类型的_partial_特化不是未定义行为吗? - user1143634
4
@StaceyGirl,我不这么认为。来自 cppreference 的引用:_"只有在声明依赖于至少一个程序定义的类型时,才允许将任何标准库[...]模板的模板专业化添加到命名空间 std 中。"_ - Timo
你是正确的,不支持ostream operator<<操作符。 - vitaut
1
@vitaut 哦,格式大师来拯救了。感谢您的编辑。从cppreference上写的内容我不太确定。 - Timo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接