## lexical_cast
### 头文件: `"boost/lexical_cast.hpp"`
所有应用都会使用字面转换。我们把字符串转为数值,反之亦然。许多用户定义的类型可以转换为字符串或者由字符串转换而来。你常常是在需要这些转换时才编写代码,而更好的方法是提供一个可重用的实现。这就是 `lexical_cast`的用途所在。你可以把`lexical_cast` 想象为使用一个 `std::stringstream` 作为字符串与数值的表示之间的翻译器。这意味着它可以与任何用`operator<<`进行输出的源以及任何用`operator<<`进行输入的目标一起工作。这个要求对于所有内建类型与多数用户自定义类型(UDTs)都可以做到。
### 用法
`lexical_cast` 在类型之间进行转换,就象其它的类型转换操作一样。当然,使它得以工作的必须是一个转换函数,但从概念上说,你可以把它视为转型操作符。比起调用一堆的转换子程序,或者是编写自己的转换代码,`lexical_cast` 可以更好地为任何满足它的要求的类型服务。它的要求就是,源类型必须是可流输出的(OutputStreamable),而目标类型必须是可流输入的 (InputStreamable)。另外,两种类型都必须是可复制构造的(CopyConstructible),并且目标类型还要是可缺省构造的 (DefaultConstructible)和可赋值的(Assignable)。可流输出(OutputStreamable)意味着存在一个为该类 型定义的`operator<<`,可流输入(InputStreamable)则要求有一个`operator>>`. 对于许多类型,包括所有内建类型和标准库中的字符串类型,这个条件都满足。要使用 `lexical_cast`, 就要包含头文件 `"boost/lexical_cast.hpp"`.
### 让 lexical_cast 工作
我不想通过跟你示范手工编写转换用的代码来说明 `lexical_cast` 如何节省了你的时间,因为我可以很肯定你一定写过这样的转换代码,并且很可能不只一次。相反,只用一个例子来示范如何使用 `lexical_cast` 来进行通用的(字面上的)类型转换。
```
#include <iostream>
#include <string>
#include "boost/lexical_cast.hpp"
int main() {
// string to int
std::string s="42";
int i=boost::lexical_cast<int>(s);
// float to string
float f=3.14151;
s=boost::lexical_cast<std::string>(f);
// literal to double
double d=boost::lexical_cast<double>("2.52");
// 失败的转换
s="Not an int";
try {
i=boost::lexical_cast<int>(s);
}
catch(boost::bad_lexical_cast& e) {
// 以上lexical_cast将会失败,我们将进入这里
}
}
```
这个例子仅仅示范了多种字面转换情形中的几种,我想你应该同意为了完成这些工作,通常你需要更多的代码。无论何时你不确定转换是否有效,都应该用一个`try/catch` 块来保护 `lexical_cast` ,就象你在这个例子看到的那样。你可能注意到了没有办法控制这些转换的格式;如果你需要这种级别的控制,你要用 `std::stringstream`!
如果你曾经手工进行过类型间的转换,你应该知道,对于不同的类型,需要使用不同的办法来处理转换以及可能出现的转换失败。这不仅是有点不便而已,它还妨碍了用泛型代码执行转换的努力。稍后我们将看到 [lexical_cast](#ch02lev1sec5) 如何帮助你实现这一点。
这个例子中的转换用手工来实现也非常简单,但可能会失去转型操作的美观和优雅。而 `lexical_cast` 做起来更简单,并且更美观。再考虑一下`lexical_cast`对与之一起工作的类型所需的简单要求。考虑到对所有符合该要求的类型的转换可以在一行代码内完成的事实。再结合该实现依赖于标准库的`stringstream`这一事实\[15\],你可以看到 [lexical_cast](#ch02lev1sec5) 不仅是执行字面转换的便利方法,它更是C++编译艺术的一个示范。
> \[15\] 事实上,对于某些转换,有一些优化的方法可以避免使用 `std::stringstream` 带来的额外开销。当然,你可以在需要的时候对你自己的类型定制它的行为。
### 用 lexical_cast 进行泛型编程
作为使用`lexical_cast`进行泛型编程的简单例子,来看一下如何用它创建一个 `to_string` 函数。这个函数接受任何类型的参数(当然它要符合要求)并返回一个表示该值的 `string` 。标准库的用法当然也可以在`std::stringstream`的帮助下用几行代码完成这个任务。在这里,我们使用`lexical_cast` 来实现,只需要一个前转换函数调用及一些错误处理。
```
#include <iostream>
#include <string>
#include "boost/lexical_cast.hpp"
template <typename T> std::string to_string(const T& arg) {
try {
return boost::lexical_cast<std::string>(arg);
}
catch(boost::bad_lexical_cast& e) {
return "";
}
}
int main() {
std::string s=to_string(412);
s=to_string(2.357);
}
```
这个小程序不仅易于实现,它还因为`lexical_cast`而增加了价值。
### 使类可以用于 lexical_cast
因为 `lexical_cast` 仅要求它所操作的类型提供适当的 `operator<<` 和 `operator>>` ,所以很容易为用户自定义类型增加字面转换的支持。一个可以同时作为`lexical_cast`的目标和源的简单UDT看起来就象这样:
```
class lexical_castable {
public:
lexical_castable() {};
lexical_castable(const std::string s) : s_(s) {};
friend std::ostream operator<<
(std::ostream& o, const lexical_castable& le);
friend std::istream operator>>
(std::istream& i, lexical_castable& le);
private:
virtual void print_(std::ostream& o) const {
o << s_ <<"\n";
}
virtual void read_(std::istream& i) const {
i >> s_;
}
std::string s_;
};
std::ostream operator<<(std::ostream& o,
const lexical_castable& le) {
le.print_(o);
return o;
}
std::istream operator>>(std::istream& i, lexical_castable& le) {
le.read_(i);
return i;
}
```
`lexical_castable` 类现在可以这样用了:
```
int main(int argc, char* argv[]) {
lexical_castable le;
std::cin >> le;
try {
int i = boost::lexical_cast<int>(le);
}
catch(boost::bad_lexical_cast&) {
std::cout << "You were supposed to enter a number!\n";
}
}
```
当然,输入和输出操作符最好可以允许这个类于于其它流。如果你使用标准库的IOStreams,或者其它使用 `operator<<` 和 `operator>>`的库,你可能已经有很多可以用于 `lexical_cast` 的类。它们不需要进行修改。直接对它们进行字面转换就行了!
### 总结
`lexical_cast` 是用于字符串与其它类型之间的字面转换的一个可重用及高效的工具。它是功能性和优雅性的结合,是杰出程序员的伟大杰作\[16\]。 不要在需要时实现小的转换函数,更不要在其它函数中直接插入相关逻辑,应该使用象 `lexical_cast` 这样的泛型工具。它有助于使代码更清晰,并让程序员专注于解决手上的问题。
> \[16\] 我知道,我总是很傲慢的,我们这些程序员,工作中常常需要数学、物理学、工程学、建筑学,和其它一些艺术和学科。这会使人畏缩,但也有无穷的回报。
以下情况时使用`lexical_cast`:
* 从字符串类型到数值类型的转换
* 从数值类型到字符串类型的转换
* 你的自定义类型所支持的所有字面转换
- 序
- 前言
- Acknowledgments
- 关于作者
- 本书的组织结构
- Boost的介绍
- 字符串及文本处理
- 数 据结构, 容器, 迭代器, 和算法
- 函数对象及高级编程
- 泛 型编程与模板元编程
- 数学及数字处理
- 输入/输出
- 杂项
- Part I: 通用库
- Library 1. Smart_ptr
- Smart_ptr库如何改进你的程序?
- 何时我们需要智能指针?
- Smart_ptr如何适应标准库?
- scoped_ptr
- scoped_array
- shared_ptr
- shared_array
- intrusive_ptr
- weak_ptr
- Smart_ptr总结
- Library 2. Conversion
- Conversion 库如何改进你的程序?
- polymorphic_cast
- polymorphic_downcast
- numeric_cast
- lexical_cast
- Conversion 总结
- Library 3. Utility
- Utility 库如何改进你的程序?
- BOOST_STATIC_ASSERT
- checked_delete
- noncopyable
- addressof
- enable_if
- Utility 总结
- Library 4. Operators
- Operators库如何改进你的程序?
- Operators
- 用法
- Operators 总结
- Library 5. Regex
- Regex库如何改进你的程序?
- Regex 如何适用于标准库?
- Regex
- 用法
- Regex 总结
- Part II: 容器及数据结构
- Library 6. Any
- Any 库如何改进你的程序?
- Any 如何适用于标准库?
- Any
- 用法
- Any 总结
- Library 7. Variant
- Variant 库如何改进你的程序?
- Variant 如何适用于标准库?
- Variant
- 用法
- Variant 总结
- Library 8. Tuple
- Tuple 库如何改进你的程序?
- Tuple 库如何适用于标准库?
- Tuple
- 用法
- Tuple 总结
- Part III: 函数对象与高级编程
- Library 9. Bind
- Bind 库如何改进你的程序?
- Bind 如何适用于标准库?
- Bind
- 用法
- Bind 总结
- Library 10. Lambda
- Lambda 库如何改进你的程序?
- Lambda 如何适用于标准库?
- Lambda
- 用法
- Lambda 总结
- Library 11. Function
- Function 库如何改进你的程序?
- Function 如何适用于标准库?
- Function
- 用 法
- Function 总结
- Library 12. Signals
- Signals 库如何改进你的程序?
- Signals 如何适用于标准库?
- Signals
- 用法
- Signals 总结