命名空間
變體
動作

聯合體 (Union) 宣告

出自 cppreference.com
< cpp‎ | language
 
 
C++ 語言
一般主題
流程控制
條件執行陳述式
if
疊代陳述式 (迴圈)
for
範圍 for (C++11)
跳躍陳述式
函式
函式宣告
Lambda 函式運算式
inline 指定符
動態例外規範 (直到 C++17*)
noexcept 指定符 (C++11)
例外
命名空間
型別
類別/結構型別
共用體型別

指定符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期指定符
初始化
 
 

聯合體 (Union) 是一種特殊的類別型別,在同一時間只能保存其非靜態 資料成員 中的其中一個。

目錄

[編輯] 語法

聯合體宣告的類別說明符與 class 或 struct 宣告相似

union 屬性 類別頭名稱 { 成員規格 }
屬性 - (C++11 起) 可選的任意數量的屬性序列
類別頭名稱 (class-head-name) - 所定義聯合體的名稱。可選擇在前方加上 嵌套名稱說明符(名稱與作用域解析運算子的序列,以作用域解析運算子結尾)。名稱可以省略,在此情況下該聯合體為未命名的。
成員規格 (member-specification) - 存取說明符、成員物件及成員函式宣告與定義的列表。

聯合體可以擁有成員函式(包括建構函式與解構函式),但不能有虛擬函式。

聯合體不能擁有基底類別,也不能作為基底類別使用。

最多只能有一個 變體成員 可以擁有 預設成員初始化器

(C++11 起)

聯合體不能擁有參考型別的非靜態資料成員。

聯合體不能包含具有非平凡(non-trivial)特殊成員函式的非靜態資料成員。

(直到 C++11)

若聯合體包含具有非平凡 特殊成員函式 的非靜態資料成員,則該聯合體對應的特殊成員函式可能會被定義為刪除(deleted),詳細資訊請參閱對應的特殊成員函式頁面。

(C++11 起)

就像在 struct 宣告中一樣,聯合體中的預設成員存取權限為 public(公開)。

[編輯] 解釋

聯合體的大小至少足以容納其最大的資料成員,但通常不會更大。其他資料成員旨在與該最大成員共享相同的位元組空間。關於該配置的細節由實作定義,但所有非靜態資料成員皆擁有相同的位址。讀取聯合體中非最近寫入的成員屬於未定義行為。許多編譯器作為非標準語言擴充功能,實作了讀取聯合體不活躍成員的能力。

#include <cstdint>
#include <iostream>
 
union S
{
    std::int32_t n;     // occupies 4 bytes
    std::uint16_t s[2]; // occupies 4 bytes
    std::uint8_t c;     // occupies 1 byte
};                      // the whole union occupies 4 bytes
 
int main()
{
    S s = {0x12345678}; // initializes the first member, s.n is now the active member
    // At this point, reading from s.s or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << std::hex << "s.n = " << s.n << '\n';
 
    s.s[0] = 0x0011; // s.s is now the active member
    // At this point, reading from s.n or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << "s.c is now " << +s.c << '\n' // 11 or 00, depending on platform
              << "s.n is now " << s.n << '\n'; // 12340011 or 00115678
}

可能輸出

s.n = 12345678
s.c is now 0
s.n is now 115678

每個成員的配置方式就如同它是該類別的唯一成員一樣。

若聯合體的成員是具有使用者自訂建構函式與解構函式的類別,則要切換活躍成員時,通常需要顯式呼叫解構函式並使用 placement new。

#include <iostream>
#include <string>
#include <vector>
 
union S
{
    std::string str;
    std::vector<int> vec;
    ~S() {} // needs to know which member is active, only possible in union-like class 
};          // the whole union occupies max(sizeof(string), sizeof(vector<int>))
 
int main()
{
    S s = {"Hello, world"};
    // at this point, reading from s.vec is undefined behavior
    std::cout << "s.str = " << s.str << '\n';
    s.str.~basic_string();
    new (&s.vec) std::vector<int>;
    // now, s.vec is the active member of the union
    s.vec.push_back(10);
    std::cout << s.vec.size() << '\n';
    s.vec.~vector();
}

輸出

s.str = Hello, world
1
(C++11 起)

若聯合體的兩個成員皆為 標準佈局 (standard-layout) 型別,在任何編譯器上檢查它們的共同子序列都是明確定義的。

[編輯] 成員生命週期

聯合體成員的 生命週期 始於該成員變為活躍時。若先前有另一個成員是活躍的,則其生命週期結束。

當聯合體的活躍成員透過形式為 E1 = E2 的指派運算式切換時,若該運算式使用內建指派運算子或平凡指派運算子,且對於每個出現在 E1 的成員存取與陣列下標子運算式中,且非具有非平凡或刪除的預設建構函式的類別之聯合體成員 X,若修改 X 會導致型別別名規則下的未定義行為,則會在指定儲存空間中隱式建立一個 X 型別的物件;不會執行初始化,且其生命週期的開始順序位於左運算元與右運算元的數值計算之後,指派之前。

union A { int x; int y[4]; };
struct B { A a; };
union C { B b; int k; };
 
int f()
{
    C c;               // does not start lifetime of any union member
    c.b.a.y[3] = 4;    // OK: "c.b.a.y[3]", names union members c.b and c.b.a.y;
                       // This creates objects to hold union members c.b and c.b.a.y
    return c.b.a.y[3]; // OK: c.b.a.y refers to newly created object
}
 
struct X { const int a; int b; };
union Y { X x; int k; };
 
void g()
{
    Y y = {{1, 2}}; // OK, y.x is active union member
    int n = y.x.a;
    y.k = 4;   // OK: ends lifetime of y.x, y.k is active member of union
    y.x.b = n; // undefined behavior: y.x.b modified outside its lifetime,
               // "y.x.b" names y.x, but X's default constructor is deleted,
               // so union member y.x's lifetime does not implicitly start
}

聯合體型別的平凡 移動建構函式、移動指派運算子、(C++11 起) 複製建構函式與複製指派運算子會複製物件表示。若來源與目的地並非同一個物件,這些特殊成員函式會在執行複製前,啟動目的地中嵌套的每個物件(不包括既非目的地子物件,亦非 隱式生命週期型別 的物件)的生命週期,這些物件對應於來源中嵌套的物件。否則,它們不執行任何操作。透過平凡特殊函式建構或指派後,兩個聯合體物件擁有相同的對應活躍成員(若有)。

[編輯] 匿名聯合體

匿名聯合體 是一種未命名的聯合體定義,且沒有同時定義任何變數(包括聯合體型別的物件、參考或指向聯合體的指標)。

union { 成員規格 } ;

匿名聯合體有進一步的限制:它們不能擁有成員函式,不能擁有靜態資料成員,且所有資料成員必須是公開的。唯一允許的宣告是非靜態資料成員 以及 static_assert 宣告(C++11 起)

匿名聯合體的成員會被注入至封閉作用域中(且不得與該處宣告的其他名稱衝突)。

int main()
{
    union
    {
        int a;
        const char* p;
    };
    a = 1;
    p = "Jennifer";
}

命名空間作用域的匿名聯合體必須宣告為 static,除非它們出現在未命名的命名空間中。

[編輯] 類聯合體類別

類聯合體類別 指的是聯合體,或是擁有至少一個匿名聯合體作為成員的(非聯合體)類別。類聯合體類別擁有一組 變體成員

  • 其成員匿名聯合體中的非靜態資料成員;
  • 此外,若該類聯合體類別本身為聯合體,則包括其非匿名聯合體的非靜態資料成員。

類聯合體類別可用於實作 標記聯合體 (tagged union)

#include <iostream>
 
// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), 
// and three variant members (c, i, d)
struct S
{
    enum{CHAR, INT, DOUBLE} tag;
    union
    {
        char c;
        int i;
        double d;
    };
};
 
void print_s(const S& s)
{
    switch(s.tag)
    {
        case S::CHAR: std::cout << s.c << '\n'; break;
        case S::INT: std::cout << s.i << '\n'; break;
        case S::DOUBLE: std::cout << s.d << '\n'; break;
    }
}
 
int main()
{
    S s = {S::CHAR, 'a'};
    print_s(s);
    s.tag = S::INT;
    s.i = 123;
    print_s(s);
}

輸出

a
123

C++ 標準程式庫包含 std::variant,它可以取代許多聯合體與類聯合體類別的用途。上述範例可以改寫為:

#include <iostream>
#include <variant>
 
int main()
{
    std::variant<char, int, double> s = 'a';
    std::visit([](auto x){ std::cout << x << '\n';}, s);
    s = 123;
    std::visit([](auto x){ std::cout << x << '\n';}, s);
}

輸出

a
123
(自 C++17 起)

[編輯] 關鍵字

union

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。

DR 應用於 出版時的行為 正確的行為
CWG 1940 C++11 匿名聯合體僅允許非靜態資料成員 static_assert 也被允許

[編輯] 參考

  • C++23 標準 (ISO/IEC 14882:2024)
  • 11.5 Unions [class.union]
  • C++20 標準 (ISO/IEC 14882:2020)
  • 11.5 Unions [class.union]
  • C++17 標準 (ISO/IEC 14882:2017)
  • 12.3 Unions [class.union]
  • C++14 標準 (ISO/IEC 14882:2014)
  • 9.5 Unions [class.union]
  • C++11 標準 (ISO/IEC 14882:2011)
  • 9.5 Unions [class.union]
  • C++03 標準 (ISO/IEC 14882:2003)
  • 9.5 Unions [class.union]
  • C++98 標準 (ISO/IEC 14882:1998)
  • 9.5 Unions [class.union]

[編輯] 參見

(C++17)
一種型別安全的識別聯合體 (discriminated union)
(類別模板) [編輯]
C 文件 關於 聯合體宣告
English Deutsch 日本語 中文(简体) 中文(繁體)