Как решить конфликт переобъявления перечисления компилятора

Рассмотрим следующие перечисления C++:

enum Identity
{
    UNKNOWN   = 1,
    CHECKED   = 2,
    UNCHECKED = 3
};

enum Status
{
    UNKNOWN    = 0,
    PENDING    = 1,
    APPROVED   = 2,
    UNAPPROVED = 3
};

Компилятор столкнулся с обоими элементами UNKNOWN и выдал эту ошибку:

ошибка: повторное объявление «НЕИЗВЕСТНО»

Я могу решить эту ошибку, изменив одно из UNKNOWN на UNKNOWN_a, но я не хотел бы менять имена.

Как разрешить этот конфликт без изменения имени enum элементов?


person Daniel Santos    schedule 16.12.2015    source источник


Ответы (4)


Для этого можно использовать перечисления с областью действия. Для этого требуется поддержка C++11 или выше.

enum class Identity
{
       UNKNOWN = 1,
       CHECKED = 2,
       UNCHECKED =3
};

enum class Status
{
       UNKNOWN = 0,
       PENDING = 1,
       APPROVED = 2,
       UNAPPROVED =3
};

int main ()
{
    Identity::UNKNOWN;
    Status::UNKNOW;
}

Живой пример

person NathanOliver    schedule 16.12.2015

Используйте область enums (C++ 11) - enum classes. Они не будут загрязнять внешнюю область повторяющимися именами.

Но вам потребуется получить доступ к перечисляемым значениям с помощью оператора разрешения области действия — Identity::UNKNOWN, что неплохо.

person LogicStuff    schedule 16.12.2015

Если использование С++ 11 невозможно (это действительно должно быть сделано сейчас, я имею в виду, что это уже 2015 год), рассмотрите возможность использования пространств имен:

namespace Identity {
enum {
       UNKNOWN = 1,
       CHECKED = 2,
       UNCHECKED =3
};
}

namespace Status {
enum {
       UNKNOWN = 0,
       PENDING = 1,
       APPROVED = 2,
       UNAPPROVED =3
};
}

Но, действительно, enum class намного лучше.

person Cássio Renan    schedule 16.12.2015
comment
@Крис, я в курсе. Это одна из проблем не просто использования enum class. Я имею в виду, что вы все еще можете просто объявить перечисления с именем (enum value { /*...*/) и использовать их как void foo(Identity::value), но вы все равно потеряете преимущества безопасности типов и т. д. - person Cássio Renan; 16.12.2015
comment
Что ж, забавно то, что C++11 довольно часто неприемлем, особенно в коммерческой сфере — например, многие клиенты компании, в которой я работаю, все еще используют старые системы, такие как RHEL, и не хотят обновляться до тех пор, пока система работает хорошо (довольно часто возникают серьезные несовместимости во время обновления версии). В результате C++11 запрещен, и все должно собираться в старых компиляторах. На самом деле C++11 был запрещен в каждой из 4 последних компаний, в которых я работал. И одна компания, в которой я раньше работал, AFAIK все еще работает в MSVC 6 :-) - person axalis; 16.12.2015

Вот как я обычно объявляю такие перечисления (если мне не нужно что-то более причудливое, например, автоматическое преобразование имен перечислений в строки, сериализация/десериализация и т. д.):

struct Identities
{
    enum Type
    {
        UNKNOWN   = 1,
        CHECKED   = 2,
        UNCHECKED = 3
    };
};

typedef Identities::Type Identity;

struct States
{
    enum Type
    {
        UNKNOWN    = 0,
        PENDING    = 1,
        APPROVED   = 2,
        UNAPPROVED = 3
    };
};

typedef States::Type Status;

// usage
Identity identity = Identities::UNKNOWN;
Status status = States::UNKNOWN;

Работает во всех версиях C++, а также является типобезопасным. Пространства имен также можно использовать вместо структур (но я обычно использую структуры).

person axalis    schedule 16.12.2015