是否为C ++标准允许的自定义类型的std :: to_string专门化?

时间:2016-04-10 17:38:55

标签: c++ c++11 std tostring specialization

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

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

示例用例:

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

4 个答案:

答案 0 :(得分:32)

  

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

没有。首先,it is not a template function所以你根本不能把它专门化。

如果您要求添加自己的重载功能,答案仍然保持不变。

来自Extending the namespace std的文档摘要:

  

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

     

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

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

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

  

17.6.4.2 命名空间使用

     

17.6.4.2.1 命名空间std

     
      
  1. 如果C ++程序向命名空间std或a添加声明或定义,则它的行为是未定义的   除非另有说明,否则命名空间std中的命程序可以添加模板专业化   对于任何标准库模板到命名空间std,仅当声明取决于用户定义的类型时   并且专业化符合原始模板的标准库要求,并未明确说明   禁止的。 181
  2.   
  3. 如果声明

    ,则C ++程序的行为是未定义的      

    2.1 - 标准库类模板的任何成员函数的显式特化,或

         

    2.2 - 标准库类或类模板的任何成员函数模板的显式特化,   或

         

    2.3 - 标准库类或类的任何成员类模板的显式或部分特化   模板。

         

    只有声明时,程序才能显式实例化标准库中定义的模板   取决于用户定义类型的名称,实例化符合标准库要求   原始模板。

  4.   
  5. 翻译单元不得将namespace std声明为内联命名空间(7.3.1)。
  6.   

答案 1 :(得分:11)

如果我没有弄错,你可以简单地重载to_string以获得泛型类型:

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

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

答案 2 :(得分:7)

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

答案 3 :(得分:4)

  

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

不,您无法在std的{​​{1}}命名空间中添加重载。

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

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

以下是:

to_string()

我们使用using declarationclass 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); } 带入范围,然后我们使用了对std::to_string的无限制调用。

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

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

to_string

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

请参阅example

注意:如果您是呼叫template<typename T> std::string my_to_string(T&& t) { using std::to_string; return to_string(std::forward<T>(t)); } 的人,则此功能正常。如果有一个名为to_string的图书馆,并且你想为你的类型更改它,那你就不走运了。