lex_cast:格式化流,未格式化

时间:2010-10-30 09:29:55

标签: c++ formatting lexical-cast

我曾经在下面看到这个很好的一小段代码,在这里:SO:

template<typename to_t, typename from_t>
to_t lex_cast(const from_t &arg) {
    to_t ret;
    std::stringstream os;
    os << arg;
    os >> ret;
    return ret;
}

这模仿boost :: lexical_cast。用法:

string tmp = ::lex_cast<string>(3.14);

但是,由于格式化流的默认跳过,以下内容将无法按预期工作:

string tmp = ::lex_cast<string>("foo bar"); // will only return foo, but I want entire string

(我期待与\ n的类似问题)。 我试图设置noskipws和模板专业化但无济于事。请指教。

1 个答案:

答案 0 :(得分:3)

实际上,要涵盖从字符串转换为字符串的所有可能情况,您需要一个相当复杂的机制。我在下面粘贴了一个可能的实现,但这肯定会让你不想使用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命名空间中存在类模板(实际上它们是结构体,但这只是为了节省我们明确地将其唯一成员公开的麻烦)。

为什么我们需要这么多专业呢?

首先,肯定需要一个 通用实现 ,它可以从任何可流式转换为任何其他类型。

然后,正如您所观察到的,我们需要一个专门化来涵盖我们想要 转换为字符串 的情况,因为在这种情况下默认实现是错误的。

两个模板参数属于同一类型 的情况相匹配的是纯优化:如果要将int转换为int,我们可以分发原始价值。你可能会问自己为什么有人想要这样做,但在模板代码中,人们不知道代码可能被调用的类型,这样的事情总是发生。

从字符串转换为任何其他类型 的专业化也是一种优化。它避免了将字符串流式传输到流中,而是直接初始化输出字符串流。这假设后者实际上比前者更快。虽然我没有测量过这个,但我认为我们可以放心地认为它永远不会慢。

留下最后一个,将 字符串“转换”为字符串 。为什么我们需要这个,是不是已经涵盖了“转换”和TT的情况?嗯,是的,它是,我们也可以使用这个,因为从语义上讲,它做的是正确的。但是编译器并不关心 语义 ,它唯一关心的是 语法 。当您想要将std::string“转换”为std::string时,编译器会找到三个与部分匹配的特化:<T,std::string><std::string,T,和<T,T>。由于它不知道在这种情况下该做什么并引发错误,我们需要通过提供 一致更好的匹配来帮助它,而不是任何这些 三。