Использовать класс и перечисление с таким же именем?

У меня есть класс и значение перечисления, которые имеют одинаковое имя. Внутри класса я хочу использовать перечисление, которое дает ошибку. Есть ли способ использовать перечисление без переименования или перемещение в другое пространство имен?

Пример:

namespace foo {
    enum bar {
        BAD
    };

    class BAD {
        void worse () {
            bar b = BAD; // error
        }
    };
};

Ответ 1

Это одна из тех сложных задач, как выполняется поиск имени.

В С++ есть две области идентификаторов: одна для типов классов и области общего идентификатора. Значение перечисления BAD находится в области общего идентификатора, тогда как тип класса BAR находится в области идентификатора класса. Вот почему вам разрешено иметь как значение перечисления, так и класс с тем же именем: оба имени не сталкиваются.

В классе BAD правила поиска идентификатора найдут класс BAD, прежде чем он найдет перечисление, и, следовательно, ошибку. Теперь, если вы полностью квалифицируете идентификатор, тогда поиск имени сначала проверяет область глобального идентификатора и соответствует значению перечисления. На противоположном конце вам нужно будет добавить ключевое слово struct или class для объявления переменных типа BAD.

namespace foo {
   enum bad { BAD; };
   class BAD {
      void worse() { bad b = ::foo::BAD; } // fully qualified will match the enum
   };
}
int main() {
   // foo::BAD b;    // error, foo::BAD is an enum, not a type
   class foo::BAD b; // correct
}

Теперь я бы советовал против этого использования. Как правило, не рекомендуется повторно использовать идентификатор, подобный этому. Код будет более сложным и, вероятно, вводит в заблуждение для случайного читателя (тот же неквалифицированный идентификатор относится к разным вещам при использовании в разных контекстах). Если имена должны быть BAD, рассмотрите возможность использования охватывающего пространства имен или класса для класса или перечисления (предпочитайте перечисление там).

Ответ 2

 bar b = foo::BAD;

или если вы находитесь в глобальном пространстве имен

 bar b = ::BAD;

но это перегрузка имени - это не то, что я бы рекомендовал. С++ 0X позволит

 bar b = bar::BAD;

что является лучшим решением, если допустима зависимость от С++ 0X.

Как и было обещано, объяснение

9,1/2

Если имя класса объявляется в области, где также объявлен объект, функция или перечислитель с таким же именем, то обе декларации находятся в области видимости, класс может быть отнесен только с использованием специфицированного спецификатора типа ( 3.4.4)

Разработанный тип-спецификатор представляет собой форму

class BAD b;

Это для совместимости с C, где имена тегов находятся в другом пространстве имен и должны быть [i] typedef [/i] ed, если вы хотите использовать их без специфицированного спецификатора типа. (Известный пример с функцией - это stat stat и stat stat в Unix).

Это объясняет, почему возможна перегрузка имени для обозначения класса и перечислителя. Причина, по которой BAD обозначает класс здесь, заключается в том, что имя класса также определено в области видимости класса, а в определении функции-члена поиск по масштабам выполняется следующим образом:  - область функций-членов  - класс  - пространство имен, содержащее определение класса

BAD находится в области класса, поэтому пространство имен foo никогда не выполняется.

Ответ 3

Нет, нет способа сделать это. Вы должны использовать действительный идентификатор. Действительный идентификатор означает, что вы должны иметь возможность идентифицировать.:)

Ответ 4

Это работает в VS2008, но дает предупреждение:

bar b = bar::BAD;

Не то, что я могу порекомендовать.

Вы должны поместить перечисление в другое пространство имен внутри foo и квалифицировать как bar, так и BAD с новым пространством имен.