列舉


有時候,你會想要定義一組相關的常數,例如,以一組常數來代表遊戲中動作:

#include <iostream> 
using namespace std; 

struct Action {
    const static int STOP = 0;
    const static int RIGHT = 1;
    const static int LEFT = 2;
    const static int UP = 3;
    const static int DOWN = 4;
};

void play(int action) {
    switch(action) {
        case Action::STOP:
            cout << "播放停止動畫" << endl;
            break;
        case Action::RIGHT:
            cout << "播放向右動畫" << endl;
            break;
        case Action::LEFT:
            cout << "播放向左動畫" << endl;
            break;
        case Action::UP:
            cout << "播放向上動畫" << endl;
            break;
        case Action::DOWN:
            cout << "播放向下動畫" << endl;
            break;
        default:
            cout << "不支援此動作" << endl;
    }
} 

int main() { 
    play(Action::RIGHT);
    play(Action::LEFT);

    return 0;
}

這種方式雖然行得通,不過 play 接受的是 int 整數,這表示你可以傳入任何 int 整數,而不一定要是列舉的數值,雖然可以透過設計,令列舉的 static 成員為 Action 的實例,並令其成為單例(singleton)等,不過,C++ 本身就提供了 enum 來達到這類任務。例如:

#include <iostream> 
using namespace std; 

enum Action {
    STOP, RIGHT, LEFT, UP, DOWN
};

void play(Action action) {
    switch(action) {
        case Action::STOP:
            cout << "播放停止動畫" << endl;
            break;
        case Action::RIGHT:
            cout << "播放向右動畫" << endl;
            break;
        case Action::LEFT:
            cout << "播放向左動畫" << endl;
            break;
        case Action::UP:
            cout << "播放向上動畫" << endl;
            break;
        case Action::DOWN:
            cout << "播放向下動畫" << endl;
            break;
        default:
            cout << "不支援此動作" << endl;
    }
} 

int main() { 
    play(Action::RIGHT);
    play(LEFT);
    play(1); // error: invalid conversion from 'int' to 'Action'

    return 0;
}

enum 列舉的成員具有型態,以上例來說,STOP 等成員都是 Action 型態,因此 play 接受是 Action 的成員,就上例來說,Action 等成員,可見範圍會與使用 enum 處的範圍相同,因此上例可以直接使用 LEFT 而不一定使用 Action:: 前置,然而,如果有其他 enum 列舉了同名的成員,省略 Action:: 就會發生名稱衝突。

enum 列舉的成員,會有預設的對應整數,無範疇的列舉成員,在必須取得整數值的場合,會自動轉換為對應的整數,對應整數預設由 0 開始,也可以自行指定。例如:

enum Action {
    STOP = 1, RIGHT, LEFT, UP, DOWN
};

就上例來說,Action::STOP 對應的整數為後續列舉成員沒有設定對應數值的話,會自動遞增 1,所以 Action::RIGHT 為 2、Action::LEFT 為 3,依此類推,然而列舉成員對應的常數值不需獨一無二,例如:

enum Action {
    STOP = 1, RIGHT, LEFT = 1, UP, DOWN
};

對於無範疇的 enum 成員,C++ 標準只保證對應的整數型態,可以容納被指定的整數值,若無法容納則編譯錯誤,不過在 C++ 11 以後可以指定型態:

enum Action : int {
    STOP, RIGHT, LEFT, UP, DOWN
};

C++ 11 以後也可以先宣告而不定義列舉成員:

enum Action : int;

C++ 11 可以定義有範疇的列舉成員,也就是可視範圍是在 enum 之內,使用時就必須加上型態前置:

#include <iostream> 
using namespace std; 

enum class Action {
    STOP, RIGHT, LEFT, UP, DOWN
};

void play(Action action) {
    switch(action) {
        case Action::STOP:
            cout << "播放停止動畫" << endl;
            break;
        case Action::RIGHT:
            cout << "播放向右動畫" << endl;
            break;
        case Action::LEFT:
            cout << "播放向左動畫" << endl;
            break;
        case Action::UP:
            cout << "播放向上動畫" << endl;
            break;
        case Action::DOWN:
            cout << "播放向下動畫" << endl;
            break;
        default:
            cout << "不支援此動作" << endl;
    }
} 

int main() { 
    play(Action::RIGHT);
    play(LEFT); // error: 'LEFT' was not declared in this scope

    return 0;
}

定義有範疇的列舉時,可以使用 classstruct,兩者等效,有範疇的列舉不會自動轉換為對應的整數值,必要時得明確指定轉型:

int action = static_cast<int>(Action::RIGHT);