为什么 std::format 和 std::vformat 的参数采用转发引用?

Pét*_*kas 3 c++ c++20 stdformat

我需要在工作中使用std::format这样的上下文:

for(std::size_t i = 0; i < ...; ++i)
{
   std::format("some string {}", i);
}
Run Code Online (Sandbox Code Playgroud)

由于循环变量i作为转发引用传递,自动 linter 会发出警告,表明它是通过引用传递的,并且可能会被修改。

在阅读了 cppreference 和std::format 提案后,似乎Formatter要求允许格式化参数为非常量,但我无法弄清楚为什么,并且草案实际上建议const Args&...参数化而不是Args&&...

作为一个实验,我尝试移动字符串,甚至专门std::formatter针对我自己的类型:

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

struct MyStruct {
    std::string a = "abc";

    MyStruct(){}
    MyStruct(const MyStruct&) { std::cout << "copy ctor" << std::endl; }
    MyStruct(MyStruct&&) noexcept { std::cout << "move ctor" << std::endl; }
    MyStruct& operator=(const MyStruct&) { std::cout << "copy assignment" << std::endl; return *this; }
    MyStruct& operator=(MyStruct&&) noexcept { std::cout << "move assignment" << std::endl; return *this; }
};

template <typename CharT>
struct std::formatter<MyStruct, CharT> : std::formatter<std::string, CharT> {
    template<typename FormatContext>
    auto format(MyStruct id, FormatContext& ctx) const {
        return std::formatter<std::string, CharT>::format(id.a, ctx);
    }
};

int main()
{
    std::string a = std::format("formatted {}", MyStruct{});
    return 0;
}

Run Code Online (Sandbox Code Playgroud)

format但即使使用参数而不是 const&进行声明lvalue,也只会调用复制构造函数(std::vformat结果std::make_format_args(MyStruct{})相同)。

所以最后一个问题是,为什么std::format需要转发引用参数,为什么不将它们限制为const&

Bri*_*ian 6

P2418添加了对格式化程序的支持,该格式化程序可以改变正在格式化的值。正如论文所解释的,最初的动机是为了std::generator类似的类型。但请注意,转发引用仅出现在参数首次传递到格式化 API 的入口点(std::formatstd::vformat等);更内部的机制(例如std::basic_format_arg)仅处理左值引用,这可能是对 a类型const或非类型的引用const