Boost C++ 函式庫

...世界上最受推崇且設計最精良的 C++ 函式庫專案之一。 Herb SutterAndrei Alexandrescu,《C++ 編碼標準

PrevUpHomeNext

第 44 章。Boost.Variant

Eric Friedman

Itay Maman

依據 Boost 軟體授權條款 1.0 版發行。(請參閱隨附檔案 LICENSE_1_0.txt 或複製於 https://boost.dev.org.tw/LICENSE_1_0.txt)

目錄

簡介
摘要
動機
教學
基本用法
進階主題
參考
概念
標頭 <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/multivisitors.hpp>
標頭 <boost/variant/get.hpp>
標頭 <boost/variant/polymorphic_get.hpp>
標頭 <boost/variant/bad_visit.hpp>
標頭 <boost/variant/static_visitor.hpp>
標頭 <boost/variant/visitor_ptr.hpp>
設計概述
「永不為空」保證
雜項說明
Boost.Variant vs. Boost.Any
可移植性
疑難排解
致謝
參考文獻

簡介

摘要

variant 類別樣板是一個安全、通用、基於堆疊的可區分聯合容器,提供了一個簡單的解決方案,以統一的方式操作來自異質類型集合的物件。雖然像 std::vector 這樣的標準容器可以被認為是「多值、單一類型」,但 variant 是「多類型、單一值」。

boost::variant 的顯著特性包括:

動機

問題

很多時候,在開發 C++ 程式時,程式設計師會發現自己需要以統一的方式操作幾種不同的類型。事實上,C++ 通過其 union 關鍵字直接提供了對這類類型的語言支援。

union { int i; double d; } u;
u.d = 3.14;
u.i = 3; // overwrites u.d (OK: u.d is a POD type)

然而,C++ 的 union 建構在物件導向環境中幾乎沒有用處。這個建構進入語言的主要目的是為了保持與 C 的兼容性,C 只支援 POD (Plain Old Data) 類型,因此不接受表現出非平凡建構或解構的類型。

union {
  int i;
  std::string s; // illegal: std::string is not a POD type!
} u;

顯然需要另一種方法。典型的解決方案是動態分配物件,然後通過一個通用的基底類型(通常是一個虛基底類別 [Hen01] 或者,更危險的是,一個 void*)來操作物件。然後可以通過多型向下轉型建構(例如,dynamic_castboost::any_cast 等)來檢索具體類型的物件。

然而,由於以下原因,這類解決方案非常容易出錯:

  • 向下轉型錯誤無法在編譯時期偵測到。 因此,不正確使用向下轉型建構將導致只能在執行時期偵測到的錯誤。
  • 可能會忽略新增的具體類型。 如果在層次結構中新增了新的具體類型,現有的向下轉型程式碼將繼續按原樣工作,完全忽略新類型。因此,程式設計師必須手動定位和修改多個位置的程式碼,這通常會導致很難找到的執行時期錯誤。

此外,即使正確實作,這些解決方案由於使用了堆積、虛擬函式呼叫和多型向下轉型,往往會導致相對顯著的抽象代價。

解決方案:一個動機範例

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