2016-04-10 3 views
29

В C++ 11 и более поздних версиях разрешено специализировать std::to_string в пространстве имен std для пользовательских типов?Является специализацией std :: to_string для пользовательских типов, разрешенных стандартом C++?

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

Пример варианта использования:

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

ответ

28

В C++ 11 и более поздних версиях разрешено специализировать std :: to_string в пространстве имен std для пользовательских типов?

Нет. Прежде всего, it is not a template function, поэтому вы не можете специализироваться на нем.

Если вы спрашиваете о добавлении собственных функций перегрузки, ответ остается неизменным.

Документация отрывок из Extending the namespace std:

Это неопределенное поведение, чтобы добавить декларации или определения патезрася или любое пространству имен, вложенные в станде, с несколькими исключениями, указанных ниже

Разрешается добавлять специализированные шаблоны для любого стандартного шаблона библиотеки для пространства имен std, только если объявление зависит от пользовательского типа, и специализация удовлетворяет всем требованиям для исходного шаблона, за исключением случаев, когда такие специализации запрещены.


На практике все будет, вероятно, прекрасно работают, но, строго говоря, стандарт говорит, что нет никакой гарантии того, что будет происходить.


Edit: У меня нет доступа к официальному стандарту, так что следующее из free working draft (N4296) в:

17.6.4.2 использования пространств имен

17.6.4.2.1 Пространство имён std

  1. Поведение Программа C++ не определена, если она добавляет объявления или определения в пространство имен std или в пространство имен в пространстве имен std, если не указано иное. Программа может добавить специализацию по шаблону для любого стандартного шаблона библиотеки в пространство имен std только в том случае, если объявление зависит от определенного пользователем типа , а специализация соответствует стандартным требованиям библиотеки для исходного шаблона и явно не указывается .
  2. Поведение программы ++ в C не определен, если он объявляет

    2,1 - явная специализация любой функции члена стандартного шаблона класса библиотеки или

    2,2 - явная специализация любого шаблон функции-члена стандартного класса библиотеки или шаблона класса, или

    2.3 - явная или частичная специализация любого шаблона класса классов стандартного класса библиотеки или класса .

    Программа может явно экземпляр шаблона, определенный в стандартной библиотеке, только если декларация зависит от имени определяемого пользователем типа и конкретизация соответствует стандартным требованиям библиотеки для исходного шаблона.

  3. Единица перевода не должна объявлять пространство имен std внутренним пространством имен (7.3.1).
+2

Без гарантии .. Не определено? Я не могу поверить, что это неопределенное поведение. Специализация для любого другого пространства имен работает просто отлично. Я очень сомневаюсь, что будут какие-либо последствия с точки зрения поведения программы. Я думаю, что стандарт должен просто сказать, что это запрещено и не называть его неопределенным, если это действительно так. Как это может привести к неопределенному поведению? – Brandon

+10

@Brandon: Обычно мы ожидаем, что компиляторы будут ошибочны, когда мы будем запрещать вещание, но требовать от компиляторов обнаружить и диагностировать подобные вещи нереально. Следовательно, UB. –

+0

Это печально. Хотелось бы, чтобы они сделали исключение для 'std :: to_string'. Или в качестве альтернативы, предоставить некоторые другие средства для ее специализации, например. используя что-то вроде 'template auto f (T && t) -> std :: enable_if_t > :: value, void> {ToStringSpec >() (std :: forward (t)); } ' – jotik

9

Если я не ошибаюсь, вы можете просто перегрузить to_string для универсального типа:

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

и это позволяет использовать ADL (аргумент зависимый поиск) вашей программой, чтобы правильно выбрать соответствующий метод to_string, основанный на переданном типе.

+1

Вы предлагаете это сделать в пространстве имен 'std' или за его пределами? –

+5

@JamesAdkison Вне этого – ArchbishopOfBanterbury

+1

Это будет работать только для неквалифицированных вызовов функций, таких как 'to_string (myObject)', но не для квалифицированных вызовов функций типа 'std :: to_string (myObject)'. – jotik

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 
}