我更新了一些旧代码以使用std::format
,并惊讶地发现它仍然有效,尽管我忘记std::formatter
为该类型提供专业化。
我立即制作了一个小测试程序来尝试重现这个问题,但正如我所料,它们总是出现编译时错误。
经过数小时的调试,我发现,如果自定义类型具有公共begin
方法end
,则库将把序列格式化为用方括号括起来的逗号分隔的列表。
问:这是标准定义的功能std::format
还是实现错误?(或者其他什么?)
这是一个独立的复制品:
#include <array>
#include <print>
class MyType {
public:
MyType() : m_values{1, 2, 3, 4} {}
using internal_type = std::array<int, 4>;
using const_iterator = typename internal_type::const_iterator;
const_iterator cbegin() const { return m_values.cbegin(); }
const_iterator cend() const { return m_values.cend(); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
private:
internal_type m_values;
};
int main() {
MyType foo;
// Since MyType is a user-defined type, I would not
// expect this print statement to compile without a
// specialization of std::formatter, but because
// it's iterable, it prints: "foo = [1, 2, 3, 4]\n".
std::print("foo = {}\n", foo);
return 0;
}
我正在使用 Visual Studio 17.12.15 中的 MS VC++ 并使用 进行编译/std:c++latest
。
标准库从 C++23 开始定义了
std::formatter
范围的专门化:此范围格式化程序针对序列、集合、映射和字符串有几种不同的变体。默认使用的规则如下:
R::key_type
和R::mapped_type
被定义,并且是或std::remove_cvref_t<std::range_reference_t<R>>
的特化,大小为 2,那么它是一个映射std::pair
std::tuple
R::key_type
有效并且是类型,那么它就是一个集合std::format_kind
您可以通过专门为您的类型指定的模板来控制默认选择哪一个,即s
您可以分别使用、?s
和说明符通过格式字符串明确选择字符串(带或不带引号和转义序列)或映射格式类型m
:此外,
n
格式说明符可用于省略除字符串之外的范围类型的括号。这m
也可以与说明符组合使用:现场演示
请注意,截至撰写本文时(2025 年 4 月 9 日),libstdc++(GCC 使用的标准库)尚未实现此功能。