【问题标题】:Why is the `std::sto`... series not a template?为什么 `std::sto`... 系列不是模板?
【发布时间】:2016-10-21 14:45:20
【问题描述】:

我想知道std::sto系列(例如std::stoistd::stol)不是函数模板是否有原因,像这样:

template<typename T>
T sto(std::string const & str, std::size_t *pos = 0, int base = 10);

然后:

template<>
int sto<int>(std::string const & str, std::size_t *pos, int base)
{
    // do the stuff.
}

template<>
long sto<long>(std::string const & str, std::size_t *pos, int base)
{
    // do the stuff.
}

/* etc. */

在我看来,这将是一个更好的设计,因为目前,当我必须将字符串转换为用户想要的任何数值时,我必须手动管理每个案例。

有没有理由没有这样的模板功能?是否有假设的选择,还是只是这样做?

【问题讨论】:

  • 我猜std::sto是指std::stoistd::stol等?
  • 浮点版本没有base 参数。
  • 可能与这些函数调用的“旧”C 函数保持一致(strtolstrtoll、...)。
  • sto&lt;long&gt;而不是stol会有什么好处?
  • @molbdnilo:您不能向std 添加新的重载,但您可以专门化现有的std 模板。例如,std::swapstd::hash

标签: c++ c++11


【解决方案1】:

看着description of these functions at cppref,我注意到以下几点:

... 解释字符串 str 中的有符号整数值。

1) 致电std::strtol(str.c_str(), &amp;ptr, base)...

strol 一个“C”标准函数,也可以在 C++ 中使用。

进一步阅读,我们看到:(对于 c++ sto* 函数):

返回值

转换为指定有符号整数类型的字符串。

例外情况

  • std::invalid_argument 如果无法进行转换
  • std::out_of_range 如果转换后的值将超出结果类型的范围或基础函数(std::strtol 或 std::strtoll) 将 errno 设置为 ERANGE。

因此,虽然我没有这方面的原始来源,并且确实从未使用过这些函数,但我会猜测

TL;DR:这些函数是围绕现有 C/C++ 函数的 C++ 式包装器 -- strtol* -- 因此它们尽可能地类似于这些函数。

【讨论】:

    【解决方案2】:

    I have to manage manually each case. Is there a reason to not have such a template function?

    对于此类问题,Eric Lippert (C#) 通常会这样说:

    如果缺少某项功能,那么它就丢失了,因为还没有人实现它。那是因为之前没有其他人想要,或者因为它被认为不值得努力,或者因为它在发布当前版本之前无法完成”。

    在这里,我想这是“不值得”的部分,但我既没有向委员会询问,也没有设法在旧问题和常见问题解答中找到任何答案。不过我并没有花太多时间搜索。

    我这样说是因为我认为这些函数中最常见的功能(如果不是全部)已经包含在流类中,例如istringstream。就像cin/etc 一样,这个也有一个万能的operator &gt;&gt;,为所有基本数字类型(以及更多)重载。

    此外,像std::hex (std::setbase) 这样的流操纵器已经解决了将各种依赖于类型的配置参数传递给实际转换函数的问题。 混合函数签名没有问题(就像 DavidHaim 在他的回答中提到的那样)。这里只是一个operator&gt;&gt;

    所以.. 因为如果我们在streams 中有它,如果我们已经可以使用简单的foo &gt;&gt; bar &gt;&gt; setbase(42) &gt;&gt; baz &gt;&gt; ... 从字符串中读取数字/等,那么我认为向旧的 C 运行时添加更复杂的层是不值得的功能。

    虽然没有证据。只是预感。

    【讨论】:

      【解决方案3】:

      模板特化的问题在于,特化要求你匹配原始模板函数签名,所以每个特化都必须实现(string,pos,base)的接口。

      如果你想拥有一些不遵循这个接口的其他类型,你就有麻烦了。

      假设将来我们想要sto&lt;std::pair&lt;int,int&gt;&gt;。我们希望将posbase 用于第一个和第二个字符串化整数。我们希望签名采用string,pos1,base1,pos2,base2 的形式。由于sto签名已经设置,我们不能这样做。

      对于整数类型,您始终可以将 std::sto* 包装在您的 sto 实现中,但您不能反过来这样做。

      【讨论】:

      • 这不是c++11中的可变参数模板的解决方法吗?
      • 你甚至可以在运行时多态之前做到这一点。然而,我认为转换函数有点矫枉过正,大多数时候你不知道编译时嵌入在字符串中的类型。通常,字符串到 XX 的转换函数用于解析(CSV、XML、JSON 等),因此无论如何您的代码将被拆分以单独处理整数、字符串等。
      • 你可能是对的,但我仍然觉得函数与 std::to_string 对称,即使它们是模板化的......
      • 好吧,老实说,我认为标准的最佳做法是丢弃任何与字符串相关的内容并从头开始重新编写。 std::sto 是冰山一角。
      【解决方案4】:

      这些函数的目的是为常见情况提供简单转换。它们并非旨在作为通用转换套件。 std::ostringstream 更适合这种事情。

      【讨论】:

        【解决方案5】:

        在我看来,会有更好的设计,因为目前, 当我必须将字符串转换为用户的任何数值时 想要,我必须手动管理每个案例。

        不,它不会。模板的目标(故意将 T-MP 分开)不是替代重载;你应该总是喜欢重载而不是模板。实际上,这是语言已经为你做的事情!在候选函数和可能的模板实例之间,前者将是首选。为了它而使用语言特性是不好的。

        我也看不出模板有什么帮助。无论用户决定输入什么类型,直到运行时才知道,模板类型在编译时推导出来。 C++ 是一种静态类型语言。在这种情况下,模板只会比正常的函数重载增加一层不必要的复杂性。

        【讨论】:

        • 我同意这种观点,但重载不是一种选择。你不能只在返回类型上重载。
        猜你喜欢
        • 2012-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多