实际上,要涵盖从字符串转换为字符串的所有可能情况,您需要一个相当精细的机制。我在下面粘贴了一个可能的实现,但这肯定会留下你为什么不想使用的问题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模板偏特化相反,因为这是可用的。这是通过创建类模板并在它的公共静态成员函数中实现算法来完成的。然后可以对类模板进行部分特化,并且每个特化都可以带有自己的静态成员函数实现。
所以这就解释了为什么在命名空间中有类模板(实际上它们是结构,但这只是为了省去我们显式将它们的唯一成员公开的麻烦)detail
。
为什么我们需要这么多这些专业?
嗯,首先,当然需要一个通用的实现,将任何可流式类型转换为任何其他类型。
然后,正如您所观察到的,我们需要一种专门化来涵盖我们想要转换为 string的情况,因为在这种情况下默认实现是错误的。
匹配两个模板参数属于同一类型的情况是一种纯粹的优化:如果你想将 an 转换int
为 an int
,我们可以直接分发原始值。你可能会问自己,到底为什么会有人想要这样做,但是在模板代码中,人们不知道可以使用哪种类型调用代码,这样的事情总是会发生。
从字符串转换为任何其他类型的专业化也是一种优化。它避免将字符串流式传输到流中,而是直接初始化输出字符串流。这假定后者实际上比前者快。虽然我没有对此进行测量,但我认为我们可以放心地假设它永远不会变慢。
剩下最后一个,它将一个字符串“转换”为一个字符串。为什么我们需要这个,那个“转换”和“转换”的案例不是已经涵盖了T
吗T
?嗯,是的,它是,我们也可以使用这个,因为从语义上讲,它做了正确的事情。但是编译器并不关心语义,它唯一关心的是语法。当您想将 a “转换”std::string
为 astd::string
时,编译器会找到三个都部分匹配的特化:<T,std::string>
、<std::string,T
和<T,T>
. 由于它不知道在这种情况下该做什么并引发错误,因此我们需要通过提供比这三个中的任何一个都更好的匹配来帮助它。