为什么 std::to_string() 没有使用模板?

4

此处所述,std::string并不是一个模板函数,而是标准选择使用函数重载来为不同的类型提供该函数。我的问题是,当在这种情况下,使用模板/特化似乎更有意义时,为什么要使用重载?考虑到如果标准定义了像这样的内容:

template <typename T>
std::string std::to_string(const T& v);

我们可以自由地为程序中的任何类型添加特化,以符合此签名,从而使C++具有将类型转换为可读字符串的统一方式。为什么不这样做呢?当前设计背后的思考是什么?
我对当前设计的主要批评是不允许向std添加重载,因此我们不能编写类似于std:to_string(用户定义类型对象)的任何内容,并且必须退回到在它们自己的命名空间中定义一个to_string()并记住在处理不同类型时使用自己的版本或std版本... 对我来说听起来像个头疼问题。
我非常喜欢Python(或其他一些语言)的一点,即您可以通过实现一些魔术方法使自己的类型像本地类型一样工作。我认为这个问题的根本就是为什么C ++决定禁止人们为自己的类型实现std :: to_string() ,从而禁止我们在所有地方符合相同的接口。
对于像哈希或to_string()之类的常见事物,是否最好在语言/stdlib级别上拥有单个接口,然后期望用户遵守该接口,而不是拥有多个接口?

2
@john 添加模板特化是一种例外情况,您可以更改 std 命名空间。 - bartop
1
为什么人们认为C++中的每一个小东西都应该使用模板?[模板特化VS函数重载] (https://dev59.com/2Gw05IYBdhLWcg3wqzes#7108123) - user1810087
2
从设计角度来看,只有在可能为无限数量的任意类型指定该函数的行为时,才有意义对该函数进行模板化。标准只能针对它所支持的类型(基本数字类型)做到这一点。它可以指定的集合是有限的,这意味着仅针对这些类型进行重载是适当的,而不是使用模板。 - Peter
@Peter "[...]它可以指定的集合是有限的,这意味着仅对那些类型进行重载才是适当的,而不是模板[...]" 这取决于to_string背后的想法。它也可以类似于beginend一样指定,例如查找to_string成员函数。毫无疑问,对象可以被转换为字符串的一般方式是有争议的,这可能是这样做的原因。 - t.niese
2
你不需要为std添加重载。它适用于swap,也适用于其他函数。只要ADL能够找到它,你可以将重载放在任何地方,答案详细解释了这一点。你看到的缺点并不存在。 - 463035818_is_not_a_number
显示剩余11条评论
2个回答

5
为什么C++禁止人们为自己的类型实现std::to_string,这就是ADL很有用的地方。我们已经有了一个正确使用std::swap的示例,在许多代码库中已经成功实现。
template <typename T>
void swap_example(T & a, T & b) {
    using std::swap;
    swap(a, b);
}

如果命名空间T中声明了兼容的swap()函数,那么这将起作用,无需重载std::swap。 我们可以使用std::to_string来做同样的事情:

template <typename T>
void to_string_example(T const & x) {
    using std::to_string;
    to_string(x);
}

如果命名空间 T 中声明了一个能够接受 T const & 参数的 to_string 函数,那么这个例子同样适用。例如:

namespace example {
    class Foo;

    std::string to_string(Foo const &);
}

to_string_example(example::Foo{})会查找并使用相应的example::to_string函数。


记住何时使用它们自己的版本或std版本取决于他们所处理的类型...这听起来像是给我带来麻烦。

如果这对您确实是一个麻烦,您可以在您的项目中隐藏ADL,使用一个实用函数:

template <typename T>
std::string adl_to_string(T const & x) {
    using std::to_string;
    return to_string(x);
}

现在你可以在任何地方使用adl_to_string(...)代替std::to_string(...),而无需考虑它。

2
这可能听起来有点无聊,但 std::to_string 的目的是格式化,就像你使用了 sprintf 一样。由于 sprintf 仅支持有限的类型集,因此这也适用于 std::to_string。没有必要将其设置为模板。
正如在这个答案中详细解释的那样,该设计并没有您认为的限制。你仍然可以提供自己的 foo::to_string(foo::bar&) 并在正确限定名称的代码中启用 ADL ,从而调用你的重载。为此,不需要添加重载到 std

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接