## 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 總結