摘要訊息 : C++ 20 Proposal P1099R5《Using Enum》導讀

C++ 20 Proposal P1099R5《Using Enum》導讀

不知道大家對於列舉這個東西是什麼感受, 不過對於名稱空間, 我想大家在保證沒有名稱衝突的情況下, 會使用 using namespace 來提升可視範圍. 否則, 在可以保證沒有名稱衝突的情況下, 不斷寫出 std::cv:: 等等名稱空間難免讓人感到煩惱. 但是對於列舉, 你沒有任何辦法避免列舉名稱的書寫 :

enum class color {
    red, green, blue, white, black
};
string to_string(color c) {
    switch(c) {
        case color::red :
            return "red";
        case color::green :
            return "green";
        case color::blue :
            return "blue";
        case color::white :
            return "white";
        default :
            return "black";
    }
}

在函式 to_string 中, 我們重複了 4 次 color, 而且這種情況還會隨著列舉中的列舉值增加而變得更加嚴重. 因此在可能的情況下, 有些人會拒絕使用強型別列舉, 而改用弱型別列舉, 他們並不願意多打這麼多字, 哪怕是複製也是比較麻煩的. 除此之外, 列舉和名稱空間非常相似, 列舉值只是不會存儲的帶有 staticconstexprinline 的 "變數" 罷了, 為什麼就不能像名稱空間一樣直接提升可視範圍呢?

於是, 這篇 Proposal 就提出, 像 using namespace 那樣 using enum :

using enum ENUM_NAME;

其中, ENUM_NAME 是某一個列舉的名稱. 將這個特性用在函式 to_string 中, 就可以打少很多字 :

enum class color {
    red, green, blue, white, black
};
string to_string(color c) {
    //using enum color;     //放在這裡也可以
    switch(c) {
        using enum color;
        case red :
            return "red";
        case green :
            return "green";
        case blue :
            return "blue";
        case white :
            return "white";
        default :
            return "black";
    }
}

對於名稱空間, using 可以做的不只是這些, 你可能還想到我們可以只提升名稱空間中某一個名稱的可視範圍, 對於列舉來說, 當然也可以 :

using enum ENUM_NAME::ENUM_VALUE;

其中, ENUM_NAME 是某一個列舉的名稱, ENUM_VALUE 是位於 ENUM_NAME 中的一個列舉值

列舉名稱是一個型別, 那麼它可以起別名, 那麼如果對某個列舉的別名使用 using 來提升可視範圍, 這也是行得通的, 兩者並不矛盾

我們上面說的都是強型別的列舉, 但是 using 指示對於弱型別的列舉同樣適用 :

struct Foo {
    enum E {
        A, B, C, D, E, F
    };
};
int main(int argc, char *argv[]) {
    using Foo::E;       //OK
}

這篇 Proposal 還給出了重複宣告的情形. 對於類別來說, 重複宣告是不被允許的 :

enum E {
    x
};
struct S {
    enum H {
        y
    };
    enum class K {
        z
    };
    using E::x;     //OK
    using E::x;     //Error, 重複宣告
    using H::y;     //Error, 重複宣告
    using K::z;     //OK
};

但是對於名稱空間來說, 重複宣告是可以的 :

enum E {
    x
};
namespace S {
    enum H {
        y
    };
    enum class K {
        z
    };
    using E::x;     //OK
    using E::x;     //OK
    using H::y;     //OK
    using K::z;     //OK
};

但是對於弱型別列舉來說, 這並不是新引入的, Proposal 只是在這裡作了一個提醒而已 :

#include <iostream>

using namespace std;
enum E {
    x = 2020
};
namespace NS {
    using ::E::x;
}
int main(int argc, char *argv[]) {
    cout << NS::x << endl;      //OK, 輸出 : 2020
}

名稱空間並不能一次引入多個, 也就是說下列陳述式將引起編碼錯誤 :

using namespace A, B, C;        //Error

但是針對列舉來說, 可以使用逗號一次性提升多個列舉值的可視範圍 (這個同樣不是新引入的, 而是原來就有的, Proposal 只是在這裡作一個提示) :

#include <iostream>

using namespace std;
enum E {
    x = 2020, y, z
};
namespace NS {
    using ::E::x, ::E::y, ::E::z;
}
int main(int argc, char *argv[]) {
    cout << NS::x << endl;      //輸出 : 2020
    cout << NS::y << endl;      //輸出 : 2021
    cout << NS::z << endl;      //輸出 : 2022
}

當然, 於名稱空間對應, 這篇 Proposal 提出使用 using 指示一次性提升多個列舉的可視範圍是不可行的. 也就是說, 下列程式碼同樣會引發編碼錯誤 :

using enum A, B, C;        //Error

最後, 這篇 Proposal 還提出, 使用 using 提升列舉值的可視範圍的時候, 列舉型別並不會被引入 :

enum class E {
    x = 2020, y, z
};
struct Foo {
    using enum E;       //引入了名稱 x, y 和 z, 但是並沒有引入名稱 E
};