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

坚强是说给别人听的谎言 提交于 2019-12-18 13:53:10

问题


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

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

Sample use-case:

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

回答1:


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

No. First of all, it is not a template function so you can't specialize it at all.

If you're asking about adding your own overload functions the answer still remains the same.

Documentation snippet from 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.


In practice everything will probably work just fine but strictly speaking the standard says there is no guarantee of what will happen.


Edit: I don't have access to the official standard so the following is from the 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).



回答2:


If I'm not mistaken you can simply overload to_string for a generic type:

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

and this allows use of ADL (argument dependent lookup) by your program to correctly choose the relevant to_string method based upon the type passed.




回答3:


Better way would be to create your own function that make use of std::to_string if it's possible as well as the .toString() method whenever it's available for passed argument:

#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
}



回答4:


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

No, you can't add an overload into the std namespace for to_string().

The good news is that you don't need to, there is a simple solution!

You can provide your own implementation and let ADL (argument dependent lookup) solve the problem for you.

Here's how:

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

Here we used the using declaration to bring std::to_string into the scope, and then we used the unqualified call to to_string().

Now both std::to_string and ::to_string are visible and the compiler picks the appropriate overload.

If you don't want to write using std::to_string before using to_string every time or you fear you will forget to use to_string without the namespace you can create an helper function

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

Note that this function can be defined in any namespace and works independently of the namespace in which the classes are defined (they don't have to be the same).

See the example.

NOTE: this works if you are the one calling to_string. If there is a library that calls std::to_string and you want to change it for your types you are out of luck.



来源:https://stackoverflow.com/questions/36533199/is-specialization-of-stdto-string-for-custom-types-allowed-by-the-c-standard

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!