Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Chapter 25. Boost.Variant

Eric Friedman

Itay Maman

Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice.

This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

目錄

簡介
概要
動機
指南
基本用法
高級議題
參考
概念
頭文件 <boost/variant.hpp>
頭文件 <boost/variant/variant_fwd.hpp>
頭文件 <boost/variant/variant.hpp>
頭文件 <boost/variant/recursive_variant.hpp>
頭文件 <boost/variant/recursive_wrapper.hpp>
頭文件 <boost/variant/apply_visitor.hpp>
頭文件 <boost/variant/get.hpp>
頭文件 <boost/variant/bad_visit.hpp>
頭文件 <boost/variant/static_visitor.hpp>
頭文件 <boost/variant/visitor_ptr.hpp>
設計概覽
"非空" 保證
雜項說明
Boost.Variant 與 Boost.Any
可移植性
疑難解答
鳴謝
參考書目

簡介

概要

variant 類模板是一個安全的、泛型的、基於棧的、可識別的聯合容器,為以統一風格操作異類類型集合的對象提供了一個簡單的方法。像 std::vector 這樣的標準容器可以被視為 "多值,單一類型",而 variant 則是 "多類型,單值"。

boost::variant 的主要特性包括:

動機

問題

很多時候,在開發一個C++程序時,程序員會發現需要以統一的風格來操作多個不同的類型。確實,C++以 union 關鍵字直接提供了這種支持:

union { int i; double d; } u;
u.d = 3.14;
u.i = 3; // 覆寫 u.d (OK: u.d 是一個 POD 類型)

但是,C++的 union 結構在面向對象的環境下幾乎是不可用的。該結構得以保存下來,主要是為了與C兼容,它只能支持 POD (Plain Old Data) 類型,不可以使用帶有非平凡構造或析構的類型:

union {
int i;
std::string s; // 非法: std::string 不是一個 POD 類型!
} u;

顯然,我們需要另一種方法。典型的方法是對對像進行動態分配,隨之而來就是要通過一個公共基類(通常是一個虛基類[Hen01] 或者是更危險的 void*)來進行操作。然後通過一個多態的向下轉型結構(如 dynamic_cast, boost::any_cast, 等等)來取回具體類型的對象。

但是,這類方法是非常容易出錯的,因為以下原因:

  • 向下轉型的錯誤不能在編譯期被檢測到。因此,對向下轉型結構的錯誤使用將導致只能在運行期被檢查出來的 bug。
  • 新加入的具體類可能被忽略。如果一個新的具體類被加入到類層次中,已有的向下轉型代碼可以繼續工作,完全忽略掉了新的類型。因此,程序員必須手工定位和修改多處代碼,這通常會導致運行期錯誤且難以覺察。

此外,即使被正確地實現,這類方法還是會由於使用了堆、虛擬函數調用和多態向下轉型而導致相對嚴重的開銷。

解決方法:一個例子

boost::variant 類模板以安全、簡單和高效的方法解決了這個問題。以下例子示範了如何使用這個類:

#include "boost/variant.hpp"
#include <iostream>

class my_visitor : public boost::static_visitor<int>
{
public:
int operator()(int i) const
{
return i;
}

int operator()(const std::string & str) const
{
return str.length();
}
};

int main()
{
boost::variant< int, std::string > u("hello world");
std::cout << u; // output: hello world

int result = boost::apply_visitor( my_visitor(), u );
std::cout << result; // output: 11 (i.e., length of "hello world")
}

PrevUpHomeNext