摘要訊息 : using namespace 那樣使用 using enum 引入列舉值.

0. 前言

對於名稱空間, 我想大家在保證沒有名稱衝突的情況下, 會使用 using namespace 來提升可視範圍; 否則, 在可以保證沒有名稱衝突的情況下, 不斷寫出 std::, boost::cv:: 等等名稱空間難免讓人感到煩惱. 然而對於列舉, 特別是強型別列舉, 如果要使用列舉中的列舉值, 我們總是需要寫出列舉的名稱.

C++ 20 提案 P1099R5《Using Enum》就提出讓列舉也支援 using 來提升可視範圍.

更新紀錄 :

  • 2022 年 5 月 17 日進行第一次更新和修正.

1. 提案內容

由於強型別列舉總是強制性需要在列舉值之前使用列舉名稱和可視範圍運算子進行標識, 所以即使 C++ 11 引入的強型別列舉比弱型別列舉更有優勢, 但是有部分人更加傾向於使用弱型別列舉. 這些人不願意多打一些字, 甚至連複製也不願意, 即便是現代 IDE 有很強大的補全功能. 於是, P1099R5 提出讓列舉也可以像名稱空間那樣使用 using 直接將名稱引入到某個可視範圍中 :

#include <string>

enum class color {
    red, green, blue, white, black
};

std::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 :
            break;
    }
    return "black";
}

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

列舉名稱是一個型別, 那麼它可以起別名, 那麼如果對某個列舉的別名使用 using 來提升可視範圍, 這也是行得通的, 兩者並不矛盾. 需要特別提出的是, 目前通過 using enum type::enum_name; 是不允許的, 因為編碼器無法分辨 typeenum_name 哪一個是列舉, 哪一個是型別.

當然, 弱型別列舉也支援這樣使用 using 來提升可視範圍 :

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

在名稱空間中, 多次使用 using 引入同一個名稱是允許的, 但是對於類別來說卻是不允許的 :

enum class E {
    x
};
struct S {
    using E::x;
    using E::x;     // error
};
namespace N {
    using E::x;
    using E::x;     // OK
}

這裡需要特別指出, 使用 using enum_name::enum_value; 這樣的宣告來提升列舉值的可視範圍並不是 C++ 20 引入的, 而是本來就有的.

使用 using 可以一次性引入多個名稱, 但是使用 using namespace 一次性引入多個名稱空間卻會產生編碼錯誤. 這個問題在 using enum 上不存在, 也就是說使用 using enum A, B, C; 這樣的宣告一次性引入多個列舉名稱不會產生編碼錯誤.

最後需要提醒大家, 使用 using enum 引入列舉值的時候, 列舉值對應的列舉名稱不會是不會被引入的 :

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