На самом деле, чтобы охватить все возможные случаи преобразования из строки в строку, вам нужен довольно сложный механизм.Я вставил возможную реализацию ниже, но это, безусловно, оставляет вопрос, почему вы не хотите использовать boost::lexical_cast
вместо этого.
//Beware, brain-compiled code ahead!
namespace detail {
template< typename T, typename S >
struct my_lexical_caster {
static T my_lexical_cast(const S& s) {
std::stringstream ss;
if( !(ss << s) ) throw std::bad_cast("cannot stream from source");
T t;
if( !(ss >> t) ) throw std::bad_cast("cannot stream to target");
return t;
}
};
template< typename S >
struct my_lexical_caster<std::string,S> {
static std::string my_lexical_cast(const S& s) {
std::ostringstream oss;
if( !(oss << s) ) throw std::bad_cast("cannot stream from source");
return oss.str();
}
};
template< typename T >
struct my_lexical_caster<T,std::string> {
static T my_lexical_cast(const std::string& s) {
std::stringstream ss(s);
T t;
if( !(ss >> t) ) throw std::bad_cast("cannot stream to target");
return t;
}
};
template< typename T >
struct my_lexical_caster<T,T> {
static const T& my_lexical_cast(const T& s) {return s;}
};
template<>
struct my_lexical_caster<std::string,std::string> {
static const std::string& my_lexical_cast(const std::string& s) {return s;}
};
}
template< typename T, typename S >
inline T my_lexical_cast(const S& s)
{
return detail::my_lexical_caster<T,S>::my_lexical_cast(s);
}
Так почему же это так сложно?
Сначала посмотрим, как у нас есть два параметра шаблона, один из которых определяет тип возврата из my_lexical_cast<>()
.Теперь мы должны предоставить специальные реализации для определенных специальных типов.Хотя мы могли бы перегрузить функции, основанные на различных аргументах функции, мы не можем перегрузить их, основываясь на возвращаемых значениях.Таким образом, вместо перегрузки шаблона функции нам нужно специализировать шаблон.
Однако, это тоже идет с подвохом: нет частичной специализации для шаблонов функций, только полная специализация.Обычно причиной этого является то, что вместо частичной специализации шаблона функции мы имеем перегрузку шаблонов функции .Хотя это может быть, это не помогает нам, когда используются возвращаемые типы.
Распространенный способ обойти отсутствующую частичную специализацию шаблона функции состоит в том, чтобы вместо этого использовать _class template частичную специализацию *, поскольку это доступно.Это делается путем создания шаблонов классов и реализации алгоритма в его публичной статической функции-члене.Шаблон класса затем может быть частично специализированным, и каждая специализация может иметь собственную реализацию статической функции-члена.
Таким образом, это объясняет, почему существуют шаблоны классов (на самом деле они являются структурами, но это только для того, чтобы избавить нас от необходимостиявно сделать их единственным членом публичным) в пространстве имен detail
.
И зачем нам так много этих специализаций?
Ну, во-первых, обязательно должна быть общая реализация , которая преобразуетот любого типа streamable к любому другому.
Тогда, как вы заметили, нам нужна одна специализация, чтобы охватить случай, когда мы хотим преобразовать в строку , потому что реализация по умолчанию в этом случае ошибочна,
Тот, который соответствует случаю, когда оба параметра шаблона имеют одинаковый тип , является чистой оптимизацией: если вы хотите преобразовать int
в int
, мы можем просто раздать оригинальное значение.Вы можете спросить себя, почему, черт возьми, кто-нибудь когда-нибудь захочет это сделать, но в шаблонном коде, где никто не знает, с какими типами может вызываться код, подобные вещи случаются постоянно.
Специализация для преобразования из строки в любой другой тип также является оптимизацией.Это позволяет избежать потоковой передачи строки в поток и вместо этого инициализировать поток выходной строки напрямую.Это предполагает, что последний на самом деле быстрее, чем первый.Хотя я не измерял это, я думаю, мы можем с уверенностью предположить, что это никогда не медленнее.
Остается последний, который "преобразует" строку в строку .Зачем нам этот, разве этот случай уже не охвачен тем, что «конвертирует» и T
в T
?Ну, да, это так, и мы могли бы использовать это тоже, так как семантически, он делает правильные вещи.Но компилятору не важна семантика , его единственное беспокойство - синтаксис .И когда вы хотите «преобразовать» std::string
в std::string
, компилятор находит три специализации, которые все соответствуют частично : <T,std::string>
, <std::string,T
и <T,T>
.Поскольку он не знает, что делать в этом случае и выдает ошибку, нам нужно помочь ему, предоставив единогласно лучшее соответствие, чем любой из этих три.