我知道在c 中列舉繼承是不可能的,但我正在尋找能簡單解決我的問題的具體資料結構。假設我有這兩個列舉:
enum Fruit { apple, orange};
enum Drink { water, milk};
我想要一個這兩個的父類,我可以在這個抽象方法中作為引數使用
。 void LetsEat(Eatable eatable){}。
它們將被用作簡單的開關,而且基本上我想保持我的代碼干凈和型別安全。我不知道我是否會被迫使用需要被初始化的繼承類。對于這個簡單的問題來說,這實在是太多了。
。uj5u.com熱心網友回復:
這聽起來像是std::variant的絕佳用例。
#include <variant>
#include <iostream>
//定義我們的列舉。
enum Fruit { Apple, Orange };
enum Drink { Water, Milk };
// 一個吃的是水果或飲料。
using Eatable = std::variant<Fruit, Drink> 。
void letsEat(Eatable eatable) {
//我們可以使用index()方法來計算出我們有哪一個。
switch (eatable.index()) {
case 0:
std::cout << "這是一個水果!" << std::endl;
break。
case 1:
std::cout << "這是一個飲料!" << std::endl;
break。
}
}
int main() {
letsEat(Apple)。
letsEat(Water)。
注意,std::variant<Fruit, Drink>嚴格來說,不是Fruit或Drink的一個超型別。相反,它是一個新的型別,但是我們通過它的建構式得到了從Fruit和Drink 到 std::variant<Fruit, Drink>的隱式轉換。
如果你沒有使用C 17,你可以使用Boost C 庫中的boost::variant。
uj5u.com熱心網友回復:
一般來說,enums只是裝扮好的ints。
enum Fruit { apple, orange};
如果你看一下編譯后的代碼,你會發現一個蘋果將用數值0表示,而一個橙色將用數值1表示。
enum Drink { water, milk}。
同樣的事情在這里也會發生。水將用值0表示,而牛奶將用值1表示。你可以開始看到這里有一個明顯的問題。
一個稍微原始的解決方案相當于在瓷器店里放了一頭公牛:
一個稍微原始的解決方案相當于在瓷器店里放了一頭公牛。
enum Drink { water=2, milk=3}。
現在你可以做一些事情,你傳入一個int值,并通過它的值找出到底傳入了什么。
但是這可能需要大量丑陋的轉換,無處不在。由此產生的代碼,如果被發布到 Stackoverflow 上,很可能會吸引到下拉的投票。
反駁的原因是,在現代的、后 C 17 的世界中,有一些更干凈的解決方案可用。對于初學者來說,你可以切換到enum類。
enum class Fruit { apple, orange};
enum class Drink { water, milk};
這就獲得了額外的型別安全。將一個Fruit分配給一個Drink并不那么容易。你的C 編譯器會在許多會發出警告的情況下,非常大聲地吠叫。你的C 編譯器將幫助你找到更多的錯誤,在你的代碼中。誠然,這將需要更多的型別化。你將不得不在任何地方指定具有完全資格的列舉值,即Fruit::apple和Drink::water,而在你現有的代碼中,僅僅是apple和water就足夠了。但是為了更多的型別安全的代碼,以及為了能夠簡單地宣告:
typedef std::variant<Fruit, Drink> Eatable;
并且簡單地做你一直想做的事情:
void LetsEat(Eatable eatable){}。
并且一切都會按照你的要求來運作。LetsEat將接受一個Fruit或一個Drink作為其引數。它將不得不多做一點作業,以弄清std::variant中的內容,但是從來沒有人聲稱C 是簡單的。
std::variant是C 庫中比較復雜的模板之一,不可能在Stackoverflow上用短短的一兩段話就完全解釋如何使用它。但這是可能的,我將向你推薦你的C 教科書,以獲得關于如何使用這個模板的完整描述。
uj5u.com熱心網友回復:
你可以使用std::variant<T, ...>如果你使用C 17或以上版本:
#include <iostream>
#include <variant>
#include <type_traits>
enum Fruit { apple, orange };
enum Drink { water, milk };
using Eatable = std::variant<Fruit, Drink> 。
void LetsEat(Eatable const eatable) {
std::visit([] (auto& & v) {
using T = std::decay_t<decltype(v)> 。
if constexpr (std::is_same_v<T, Fruit> ) {
//現在像使用普通的'Fruit'變數一樣使用它 ... }
}
if constexpr (std::is_same_v<T, Drink> ) {
//現在像使用普通的'Drink'變數一樣使用它......。
}
}, eatable)。
}
int main() {
LetsEat(蘋果)。
}
另外,你可以直接創建一個隱式轉換為任一enum型別的類:
class Eatable {
union {
水果 f;
飲料 d;
} u_;
bool has_fruit_;
public:
Eatable(Fruit f) : has_fruit_(true) {
u_.f = f。
};
Eatable(Drink d) : has_fruit_(false) {
u_.d = d。
};
operator Fruit() const{
return u_.f。
}
operator Drink() const {
return u_.d。
}
bool has_fruit() const {
return has_fruit_。
}
};
然后你可以這樣使用它:
void LetsEat(Eatable const eatable) {
if (eatable.has_fruit() ) {
水果 const f = eatable;
switch (f) {
case apple:
std::cout << "Fruit: apple" << std::endl;
break。
case orange:
std::cout << "Fruit: orange" << std::endl;
break。
default: break;
}
} else {
飲料 const d = eatable;
switch (d) {
case water:
std::cout << "Drink: water" << std::endl;
break。
case milk。
std::cout << "Drink: milk" << std::endl;
break。
default: break;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/320078.html
標籤:
上一篇:具有多重繼承性的單子
下一篇:繼承的C 類設計
