0

C++20 引入了以下格式函数(locale 和 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);

我的猜测是以下部分或全部:

  1. 实施的复杂性增加

  2. 增加编译时间

  3. 代码膨胀

,但我想知道在标准化过程中是否曾讨论过这个问题。

4

1 回答 1

3

强类型定义的目的是防止它起作用:

void takes_id(SomeIdType);
takes_id(42); 

的关键format是允许它工作:

format("User {} owes me {} points.", name, 100);

那是一个字符串文字。需要一个强类型意味着给用户更多的负担,不得不写这样的东西:

format(format_string("User {} owes me {} points."), name, 100);

这不是典型的强 typedef 用例的负担,因为您实际上是在贩卖SomeIdTypes. 您将拥有一个函数,该函数为您提供一个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);

尽管我们在正确的位置提供了格式字符串,但我们真的不希望它编译。而且似乎可以做到这一点。但是我们也不需要强类型定义,我们只需要知道格式字符串是否是常量表达式的能力。


总而言之,答案是:

但我想知道为什么没有接受“强类型定义”的重载

是这需要用户提供更多的调用端注释,同时提供非常小的好处。它只会在最稀有的用途中发现错误的用途,因此似乎是一个相当糟糕的权衡。

于 2020-11-20T17:29:22.050 回答