2022年8月5日 星期五

union and std::string

這個其實是我在用 bison 和 c++ 時遇到的問題, 我不想用 char*, std::string *, 想直接用 std::string, 參考 u.cpp L7, 所以才碰到這個問題。

bison 的解法是改用輸出 c++ 版本的 parser, 紀錄在「yacc/bison 系列 (3) bison 與 c++

u.cpp
 1 #include <cstdio>
 2 #include <string>
 3 using namespace std;
 4 
 5 union YYSTYPE
 6 {
 7   string id;
 8   int num;
 9   #if 0
10   YYSTYPE(){};
11   ~YYSTYPE(){};
12   YYSTYPE operator=(const YYSTYPE&){}
13   #endif
14 };
15 
16 YYSTYPE yylval;
17 
18 int main(int argc, char *argv[])
19 {
20 
21   return 0;
22 }


list 1. error message
g++ -std=c++17 u.cpp

1 u.cpp:16:9: error: use of deleted function ‘YYSTYPE::YYSTYPE()’
2    16 | YYSTYPE yylval;
3       |         ^~~~~~
4 u.cpp:5:7: note: ‘YYSTYPE::YYSTYPE()’ is implicitly deleted because the default definition would be ill-formed:
5     5 | union YYSTYPE


這個情境在 c++ primer the 5th 中文版「19.6 union:節省空間的類別」有提到, 有點複雜, 摘錄其部份內容。

具有類別型別的成員的 union (p848)

在早期版本的C++ 底下,union 不能有成員是定義了自己的建構器或拷貝控制成員的類別型 別。在新標準之下,這項限制鬆綁了。然而,具有的成員定義了它們自己的建構器或拷貝控 制成員的 union 使用起來會比成員是內建型別的 union 還要複雜。

當一個union 具有內建型別的成員,我們可以使用一般的指定來改變那個 union 所存放的值。 而成員不是簡單類別型別的 union 就不是這樣了,當我們將 union 的值切換至或切換自類別型別的一個成員,我們就必須建構或摧毀那個成員:當我們將 union 切換至類別型別的一個成員,我們就必須執行那個成員的型別的一個建構器;當我們切換自那個成員,就必須執行它的解構器。

當一個 union 有內建型別的成員,編譯器會合成逐個成員(memberwise)版的預設建構器 或拷貝控制成員,但對成員是定義有自己的預設建構器或一或多個拷貝控制成員的 union 來 我,就不是如此了。如果一個union 的成員的型別定義了這些成員其中之一,那麼編譯器就會把union 對應的成員合成為 deleted(§13.1.6)。

舉例來說, string 類別定義了所有的五個拷貝控制成員,以及預設建構器,如果一個 union 含有一個 string, 而且並沒有定義自己的預設建構器或其中一個拷貝控制成員, 那麼編譯器就會合成那個缺少的成員為 deleted, 如果一個類別有一個 union 成員具有一個 deleted 的拷貝控制成員,那麼該類別本身對應的拷貝控制運算也會是 deleted 的。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。