如何使用 C++20 的 std::format?

28

C++20引入了std::format。与printfstd::cout相比,它有什么优势? 我该如何使用它?能否举个例子?


5
请问您对于 cppreference.com 上的描述有哪些不清楚之处?具体而言,您对该描述的哪个部分有疑问? - Sam Varshavchik
3
你这里并没有在比较相似的事物。std::format 的作用更接近于 std::ostringstreamstd::sprintf,而不是 std::coutprintf - walnut
2个回答

32

与printf相比,有哪些优势

类型安全。对于printf,程序员必须仔细匹配格式说明符和参数的类型。如果他们犯了一个错误,程序的行为是未定义的。这是一个非常常见的错误源,尤其是对于初学者。

公平地说,只要使用常量格式字符串,只要程序员记得/知道如何启用警告,像样的编译器会诊断这些错误。但无论如何,使用模板参数推导自动选择格式化类型更加方便、安全。

此外,没有一种方法可以扩展printf以支持打印类类型。

还是std::cout呢

流操作符修改器相当麻烦且冗长,并且具有不一致的行为。某些修改器是“粘性的”,影响所有后续插入,而其他的则只影响单个插入。

在iostream API中,格式和参数之间缺乏分离,这可能使(预期的)结果更难理解。

我该如何使用它

要么等待您的编译器/标准库实现支持它。或者,如果不想等待,请使用原始的非标准版本。然后按照文档操作。

规范规格是C++标准。还有一些网站以更方便的格式呈现标准,包括此库。另一个很好的信息来源是标准提案。非标准版本的仓库(链接在上一个段落中)也有大量文档,尽管与标准不同。

有人能给出一个例子吗?

这里您可以看到一个例子(改编自libfmt文档):

std::string s = std::format("I'd rather be {1} than {0}.", "right", "happy");

11
iostream API 中格式和参数之间的缺乏分离,也会妨碍支持多种语言的努力(例如,名词和动词可能交换位置)。 - Jeff Garrett
3
作为旁注,那个问题早就解决了。在编写可能需要国际化的消息时,不要像这样做 format("The {} {} is broken", color, furniture),因为在英语中是 "red table",但在法语中则是不同顺序的 table rouge。相反,应选择类似于 format("由P1指定的对象,颜色为P2,已经损坏(P1={}, P2={})", furniture, color) 这样的表达方式。唯一需要更改的是完全自包含的格式字符串的语言特定部分,可以根据每种语言进行调整。变量部分的顺序是无关紧要的。 - paxdiablo
1
glibc允许设置自定义说明符以打印自定义类型。它是一个C函数,旨在与C数据类型一起使用,但由于指针只是指针,因此您可以欺骗函数相信类的指针是void指针(您需要具有ABI并使用glibc)。有趣的是,没有人提到速度,这是它的主要优点,在我看来。 - alx - recommends codidact

20

C++20的std::format主要是包含许多人已经在使用的fmt库(我们将其作为spdlog日志框架的一部分使用)。

因此,如果您想使用它,只需下载fmt即可。

至于优点,它具有流的类型安全性,但不含冗长(传统的C语言printf虽然简洁,但既不具备类型安全性也不具备可扩展性)。以下是一个稍作修改的示例,来自我们自己的代码库:

std::string idStr = fmt::format("prefix.{:05d}.suffix", id);

否则需要使用不太简洁的标准C ++:

std::string idStr;
{
    std::stringstream ss;
    ss << "prefix." << std::setfill('0') << std::setw(5) << id << ".suffix";
    idStr = ss.str();
}

5
对我来说,格式版本也更好地传达了输出的外观。 - Peter - Reinstate Monica
1
实际上,我们可以想象(并编写)一个格式操作器,例如 ss << "prefix." << std::format(":05d") << id << ".suffix";,有效地将下一个输出项的完整格式捆绑在一个操作器中。(不确定这是否可以在静态类型安全性方面完成得比 printf 更加类型安全)。 - Peter - Reinstate Monica
@Peter,我同意你的第一条评论(至少)。这也是为什么我喜欢Python f-strings的原因,即使被打印的变量也在正确的位置:f'prefix.{id:05d}.suffix' - paxdiablo
1
感谢您的编辑,@uneven_mark,如果C++23能够淘汰strstream并自动将std::sstreamstd::strstream视为std::stringstream,我会非常高兴。这将使我不必每次在需要使用它时(当然,在没有fmt的情况下)都要查找它。即使是现在,我也不确定我是否已经在这个评论中放置了正确的内容 :-) - paxdiablo
1
我甚至不知道std::strstream。我从未见过它被使用,但显然它在我开始编程之前就已经被弃用了。我甚至不知道你可以在第一个标准迭代中废弃某些东西。看着这个接口,我觉得自己很幸运没有不得不使用它。 - walnut
2
@sidenote:标准C++另一个自从(标准)概念就已被弃用的特性是将字符串字面值隐式转换为非const的char*。该转换在C++11中已被移除。 - eerorika

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