C++ 标准允许自定义类型的 std::to_string 特化吗?

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

在 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;
}

如果我没记错的话,您可以简单地为泛型类型重载 to_string

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

这允许您的程序使用 ADL(参数相关查找)根据传递的类型正确选择相关的 to_string 方法。

In C++11 and later, is it allowed to specialize std::to_string in the std namespace for custom types?

没有。首先,it is not a template function 所以你根本无法特化它。

如果您询问有关添加自己的重载函数的问题,答案仍然相同。

来自 Extending the namespace std 的文档片段:

It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below

It is allowed to add template specializations for any standard library template to the namespace std only if the declaration depends on a user-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited.


在实践中,一切都可能会正常工作,但严格来说,标准表示无法保证会发生什么。


编辑:我无法访问官方标准,因此以下内容来自 free working draft (N4296)

17.6.4.2 Namespace use

17.6.4.2.1 Namespace std

  1. The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.181
  2. The behavior of a C++ program is undefined if it declares

    2.1 — an explicit specialization of any member function of a standard library class template, or

    2.2 — an explicit specialization of any member function template of a standard library class or class template, or

    2.3 — an explicit or partial specialization of any member class template of a standard library class or class template.

    A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.

  3. A translation unit shall not declare namespace std to be an inline namespace (7.3.1).

更好的方法是创建自己的函数,如果可能的话使用 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
}

In C++11 and later, is it allowed to specialize std::to_string in the std namespace for custom types?

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

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

您可以提供自己的实现,让ADL(argument dependent lookup)为您解决问题。

方法如下:

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 declarationstd::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));
}

请注意,此函数可以在任何名称空间中定义,并且独立于定义 类 的名称空间工作(它们不必相同)。

参见example

注意:如果您是调用 to_string 的人,此方法有效。如果有一个调用 std::to_string 的库,而你想为你的类型更改它,那你就不走运了。