是C++标准允许的自定义类型的std::to_string的专用化

Is specialization of std::to_string for custom types allowed by the C++ standard?

本文关键字:to string 专用 std 类型 标准 C++ 自定义      更新时间:2023-10-16

在C++11及更高版本中,是否允许在std命名空间中为自定义类型专门化std::to_string

namespace std {
string to_string(::MyClass const & c) { return c.toString(); }
}

示例用例:

int main() {
    MyClass c;
    std::cout << std::to_string(c) << std::endl;
}

在C++11及更高版本中,是否允许在自定义类型的std命名空间中专门化std::to_string?

没有。首先,它不是一个模板函数,所以你根本不能专门化它。

如果你想添加自己的重载函数,答案仍然不变。

扩展命名空间std:的文档片段

向命名空间std或嵌套在std中的任何命名空间添加声明或定义是未定义的行为,下面指出了一些例外

只有当声明依赖于用户定义的类型并且专门化满足原始模板的所有要求时,才允许将任何标准库模板的模板专门化添加到命名空间std,除非禁止此类专门化。


在实践中,一切可能都会很好,但严格来说,标准规定不能保证会发生什么。


编辑:我无法访问官方标准,因此以下内容来自免费工作草案(N4296):

17.6.4.2命名空间使用

17.6.4.2.1命名空间标准

  1. 如果C++程序将声明或定义添加到命名空间std或命名空间std中的命名空间,除非另有指定。程序可以添加模板专用化对于任何标准库模板,仅当声明依赖于用户定义的类型时,才将其命名为std并且专业化满足原始模板的标准库要求,并且没有明确禁止181
  2. 如果C++程序声明,则其行为是未定义的

    2.1——标准库类模板或的任何成员函数的显式专门化

    2.2——标准库类或类模板的任何成员函数模板的显式专门化,或

    2.3——标准库类或类的任何成员类模板的显式或部分专用化样板

    只有当声明取决于用户定义类型的名称,并且实例化满足标准库要求用于原始模板。

  3. 翻译单元不应将命名空间std声明为内联命名空间(7.3.1)

如果我没有弄错,您可以简单地为泛型类型重载to_string

template<typename T> to_string(const T& _x) {
    return _x.toString();
}

这允许您的程序使用ADL(自变量相关查找),根据传递的类型正确选择相关的CCD_ 4方法。

在C++11及更高版本中,是否允许在自定义类型的std命名空间中专门化std::to_string?

不可以,不能将重载添加到to_string()std命名空间中。

好消息是你不需要,有一个简单的解决方案

您可以提供自己的实现,并让ADL(参数相关查找)为您解决问题。

方法如下:

class A {};
std::string to_string(const A&)
{
    return "A()";
}
int main()
{
    A a;
    using std::to_string;
    std::cout << to_string(2) << ' ' << to_string(a);
}

在这里,我们使用using声明将std::to_string引入作用域,然后使用对to_string()的非限定调用。

现在std::to_string::to_string都是可见的,编译器会选择适当的重载。

如果你不想在每次使用to_string之前编写using std::to_string,或者你担心在没有命名空间的情况下忘记使用to_string,你可以创建一个帮助函数

template<typename T>
std::string my_to_string(T&& t)
{
    using std::to_string;
    return to_string(std::forward<T>(t));
}

注意,这个函数可以在任何命名空间中定义,并且与定义类的命名空间无关(它们不必相同)。

请参见示例。

注意:如果您是调用to_string的人,则此操作有效。如果有一个库调用std::to_string,而你想根据你的类型更改它,那你就太倒霉了。

如果可能的话,最好创建自己的函数来使用std::to_string,只要.toString()方法可用于传递的参数:

#include <type_traits>
#include <iostream>
#include <string>
struct MyClass {
   std::string toString() const { return "MyClass"; }
};
template<class T>
typename std::enable_if<std::is_same<decltype(std::declval<const T&>().toString()), std::string>::value, std::string>::type my_to_string(const T &t) {
    return t.toString();
}
template<class T>
typename std::enable_if<std::is_same<decltype(std::to_string(std::declval<T&>())), std::string>::value, std::string>::type my_to_string(const T &t) {
    return std::to_string(t);
}
int main() {
   std::cout << my_to_string(MyClass()) << std::endl; // will invoke .toString
   std::cout << my_to_string(1) << std::endl; //will invoke std::to_string
}