我有两个广泛相关的问题。
我想创建一个函数,将参数转发到fmt::format(然后std::format在支持增加时转发到)。像这样的东西:
#include <iostream>
#include <fmt/core.h>
constexpr auto my_print(auto&& fmt, auto&&... args) {
// Error here!
// ~~~~~~~~v~~~~~~~~
return fmt::format(fmt, args...);
}
int main() {
std::cout << my_print("{}", 42) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
用 gcc 11.1.0 测试:
In instantiation of ‘constexpr auto my_print(auto:11&&, auto:12&& ...) [with auto:11 = const char (&)[3]; auto:12 = {int}]’:
error: ‘fmt’ is not a constant expression
Run Code Online (Sandbox Code Playgroud)
并使用 clang 12.0.1 进行测试:
error: call to consteval function 'fmt::basic_format_string<char, int &>::basic_format_string<char [3], 0>' is not a constant expression
Run Code Online (Sandbox Code Playgroud)
在库(core.h)中,它声明如下:
In instantiation of ‘constexpr auto my_print(auto:11&&, auto:12&& ...) [with auto:11 = const char (&)[3]; auto:12 = {int}]’:
error: ‘fmt’ is not a constant expression
Run Code Online (Sandbox Code Playgroud)
问题是cppreference指示第一个参数的类型未指定。所以
my_print传递参数fmt::format并仍然捕获相同类型的错误?对于任何类型的功能,是否有更通用的方法来执行此操作?std::format?对于更多上下文,我想创建一个std::format有条件调用的函数,如果不需要字符串,则完全避免格式化。如果你知道一个更好的方法来发表评论,我会非常感激。但是,我关于如何解决一般问题的问题仍然存在。
Jos*_*son 15
这是对构造函数的调用fmt::format_string需要是常量表达式,因此您的函数应该采用格式字符串作为 afmt::format_string而不是泛型类型:
template <typename... Args>
std::string my_print(fmt::format_string<Args...> s, Args&&... args)
{
return fmt::format(s, std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)
您可以使用std::vformat/fmt::vformat代替。
template <typename... Args>
constexpr auto my_print(std::string_view fmt, Args&&... args) {
return fmt::vformat(fmt, fmt::make_format_args(std::forward<Args>(args)...));
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/5YnY11vE4
正如您所注意到的,问题在于std::format(以及 的最新版本fmt::format)需要第一个参数的常量表达式。如果格式字符串对于传入的参数没有意义,它可以提供编译时错误。使用vformat是解决这个问题的方法。
显然,这回避了通常对格式字符串进行的编译时检查:格式字符串的任何错误都将表现为运行时错误(异常)。
除了提供格式字符串作为模板参数之外,我不确定是否有任何简单的方法可以规避这一点。一种尝试可能是这样的:
template <std::size_t N>
struct static_string {
char str[N] {};
constexpr static_string(const char (&s)[N]) {
std::ranges::copy(s, str);
}
};
template <static_string fmt, typename... Args>
constexpr auto my_print(Args&&... args) {
return fmt::format(fmt.str, std::forward<Args>(args)...);
}
// used like
my_print<"string: {}">(42);
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/5GW16Eac1
如果你真的想使用“normal-ish”语法传递参数,你可以使用用户定义的文字来构造一个在编译时存储字符串的类型:
template <std::size_t N>
struct static_string {
char str[N] {};
constexpr static_string(const char (&s)[N]) {
std::ranges::copy(s, str);
}
};
template <static_string s>
struct format_string {
static constexpr const char* string = s.str;
};
template <static_string s>
constexpr auto operator""_fmt() {
return format_string<s>{};
}
template <typename F, typename... Args>
constexpr auto my_print(F, Args&&... args) {
return fmt::format(F::string, std::forward<Args>(args)...);
}
// used like
my_print("string: {}"_fmt, 42);
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/dx1TGdcM9