为什么 C++20 格式没有格式字符串的强类型定义?
Why C++20 format has no strong typedef for format string?
C++20 引入了以下 format 函数(语言环境和 wstring_view 版本被忽略,因为它们不影响问题):
template<class... Args>
std::string format(std::string_view fmt, const Args&... args);
这没什么问题,但我想知道为什么没有接受“强类型定义”的重载,比如
template<class... Args>
std::string format(std::format_string fmt, const Args&... args);
我的猜测是以下部分或全部:
实施的复杂性增加
编译时间增加
代码膨胀
,但我想知道在标准化过程中是否讨论过这个问题。
强 typedef 的目的是防止这种情况发生:
void takes_id(SomeIdType);
takes_id(42);
format
的要点是让它起作用:
format("User {} owes me {} points.", name, 100);
这是一个字符串文字。要求强类型对用户来说意味着更多的负担,不得不这样写:
format(format_string("User {} owes me {} points."), name, 100);
这不是典型的强类型定义用例的负担,因为您实际上是在 SomeIdType
中进行交易。您将拥有一个为您提供 SomeIdType
的函数,您将存储一个 SomeIdType
类型的成员。基本上,实际转换的数量将是相当少的......所以在调用站点上你只需编写 takes_id(my_id)
并且代码大部分看起来是一样的,增加了安全性。
但是格式化的最常见情况是使用字符串文字,因此需要添加很多注释。
强类型的名义上的好处是抓住用户做这样的事情:
format(name, "User {} owes me {} points.", 100);
甚至:
format(name, 100);
前者似乎不太可能发生。后者当然是可能的,如果第一个参数恰好足够像字符串。但这是一个足以迫使每个人编写更多代码的常见问题吗?我不这么认为。
现在,如果字符串字面值有自己不同于 const char[N]
的类型(我真的希望他们有),那么就可以创建一个可从 std::string_literal
隐式构造的类型,但是需要 显式 从 std::string_view
构造。如果那是一回事,那么 API 可能会使用它 - 因为在常见情况下这不需要注释,并且不使用字符串文字似乎非常罕见,以至于需要显式转换似乎......好吗?
此外,关于安全问题,问题不在于传递错误类型的字符串,而在于实际上能够在其上下文中验证它的值:
format("User {} owes me {} points.", name);
我们非常希望这个不编译,即使我们在正确的位置提供了格式字符串。它似乎是 possible to do this。但是我们也不需要强大的类型定义,我们只需要知道格式字符串是否是常量表达式的能力。
总而言之,答案是:
but I wonder why is there not an overload that accepts a "strong typedef"
是这需要用户提供更多的调用端注释,同时提供非常小的好处。它只会在最罕见的用途中捕获错误的用途,因此似乎是一个相当糟糕的权衡。
C++20 引入了以下 format 函数(语言环境和 wstring_view 版本被忽略,因为它们不影响问题):
template<class... Args>
std::string format(std::string_view fmt, const Args&... args);
这没什么问题,但我想知道为什么没有接受“强类型定义”的重载,比如
template<class... Args>
std::string format(std::format_string fmt, const Args&... args);
我的猜测是以下部分或全部:
实施的复杂性增加
编译时间增加
代码膨胀
,但我想知道在标准化过程中是否讨论过这个问题。
强 typedef 的目的是防止这种情况发生:
void takes_id(SomeIdType);
takes_id(42);
format
的要点是让它起作用:
format("User {} owes me {} points.", name, 100);
这是一个字符串文字。要求强类型对用户来说意味着更多的负担,不得不这样写:
format(format_string("User {} owes me {} points."), name, 100);
这不是典型的强类型定义用例的负担,因为您实际上是在 SomeIdType
中进行交易。您将拥有一个为您提供 SomeIdType
的函数,您将存储一个 SomeIdType
类型的成员。基本上,实际转换的数量将是相当少的......所以在调用站点上你只需编写 takes_id(my_id)
并且代码大部分看起来是一样的,增加了安全性。
但是格式化的最常见情况是使用字符串文字,因此需要添加很多注释。
强类型的名义上的好处是抓住用户做这样的事情:
format(name, "User {} owes me {} points.", 100);
甚至:
format(name, 100);
前者似乎不太可能发生。后者当然是可能的,如果第一个参数恰好足够像字符串。但这是一个足以迫使每个人编写更多代码的常见问题吗?我不这么认为。
现在,如果字符串字面值有自己不同于 const char[N]
的类型(我真的希望他们有),那么就可以创建一个可从 std::string_literal
隐式构造的类型,但是需要 显式 从 std::string_view
构造。如果那是一回事,那么 API 可能会使用它 - 因为在常见情况下这不需要注释,并且不使用字符串文字似乎非常罕见,以至于需要显式转换似乎......好吗?
此外,关于安全问题,问题不在于传递错误类型的字符串,而在于实际上能够在其上下文中验证它的值:
format("User {} owes me {} points.", name);
我们非常希望这个不编译,即使我们在正确的位置提供了格式字符串。它似乎是 possible to do this。但是我们也不需要强大的类型定义,我们只需要知道格式字符串是否是常量表达式的能力。
总而言之,答案是:
but I wonder why is there not an overload that accepts a "strong typedef"
是这需要用户提供更多的调用端注释,同时提供非常小的好处。它只会在最罕见的用途中捕获错误的用途,因此似乎是一个相当糟糕的权衡。