c++ typedef перечисление другого класса?

Итак, вот моя проблема:

struct A
{
    enum A_enum
    {
        E0,
        E1,
        E2
    };
};

struct B
{
    typedef A::A_enum B_enum;
    bool test(B_enum val)
    {
        return (val == E1); // error: "E1" undeclared identifier
    }
};

Я специально не хочу говорить A::E1. Если я попробую B_enum::E1, я получу предупреждение о том, что это нестандартно. Есть ли хороший способ сделать что-то подобное?


person rlbond    schedule 23.05.2009    source источник
comment
Я специально не хочу говорить A::E1 - почему бы и нет?   -  person    schedule 23.05.2009
comment
потому что A используется в нескольких классах, и я не хочу ссылаться на несвязанный тип. В моем фактическом использовании A — это вложенный тип, который определен где-то еще, и на самом деле он больше похож на N::E::C::A::E1.   -  person rlbond    schedule 23.05.2009
comment
Затем вам нужно будет создать пространство имен и определить там перечисление. Почему бы не сделать это?   -  person Johannes Schaub - litb    schedule 23.05.2009
comment
К сожалению, ни один из ответов не объясняет, как его ввести, если вы не можете изменить файл заголовка, в котором определено mySprite. Это исходит от третьей стороны. См. также как определить перечисление внутри класса и использовать его извне.   -  person Deanna    schedule 13.07.2015


Ответы (6)


Я считаю, что A должно быть пространством имен, а не структурой.

person ChrisW    schedule 23.05.2009
comment
Ну, у A есть и другие вещи, но, может быть, было бы лучше поместить их в пространство имен. - person rlbond; 23.05.2009
comment
Вы также можете определить перечисление в глобальной области видимости... или в базовом классе, унаследованном классами, которые его используют. - person ChrisW; 23.05.2009

Помещение enum в глобальную область видимости слишком открыто, помещение их в класс может привести к нежелательной зависимости. Для перечисления не тесно связано с классом, это то, что я использую:

#define CLEANENUMS_BEGIN(name) namespace name { typedef enum {
#define CLEANENUMS_END(name) } internal_ ## name ## _e;} typedef name::internal_ ## name ## _e name ## _e;

Затем вы можете использовать в глобальном масштабе:

CLEANENUMS_BEGIN(myEnum)
    horizontal,
    vertical,
CLEANENUMS_END(myEnum)

Это более или менее эмулирует С# способ обработки области перечисления. Препроцессор выдаст такой код:

namespace myEnum
{
    enum internal_myEnum_e
    {
        horizontal,
        vertical,
    }
}
typedef internal_myEnum_e myEnum_e;

Затем данное перечисление упоминается как

myEnum_e val = myEnum::horizontal;

Надеюсь, есть лучший способ сделать это, но пока это единственное решение, которое я нашел.

person Community    schedule 23.05.2009
comment
Когда придет C++0x... когда бы это ни было... будет enum class MyEnum {... }; это работает как перечисление C# (со значениями перечисления в собственном пространстве имен). Действительно, синтаксис был взят из C++/.CLI (.NET C++), где он используется для создания перечислений .NET. - person mmmmmmmm; 23.05.2009
comment
Как вы относитесь к элементам тогда? - person rlbond; 23.05.2009
comment
вы можете сказать EnumName::Enumerator, а также можете объявить их в другом месте, используя объявление использования, например, using EnumName::Enumerator; сделать видимым без префикса EnumName:: . в любом случае это связано с новой спецификацией базового типа. для класса enum по умолчанию используется int. Вы можете изменить его с помощью класса перечисления EnumName : IntegerType { ... }; и то же самое, конечно, работает и для перечислений без области enum EnumName : int { ... }, где нет значения по умолчанию. При этом также появляется возможность прямого объявления: enum EnumName : int;. - person Johannes Schaub - litb; 23.05.2009

У меня была такая же проблема в прошлом, и вот как я ее исправил. Я хотел иметь возможность переключать реализацию библиотеки во время компиляции. Одна из библиотек использовала такой код:

namespace Lib1 
{
  enum LibEnum { One, Two, Three };
  [...]
  void someFunc(LibEnum val);
}

В своем коде я хотел скрыть реализацию библиотеки от взаимодействия с пользователем (поэтому пользователь моего кода никогда не должен видеть, какую библиотеку я использую внутри):

Решение 1:

namespace MyCode 
{
  // Example to avoid copying a function from Lib1 here
  typedef Lib1::someFunc aFunctionImUsing;

  // This doesn't work
  // typedef LibEnum MyEnum; 
  // As such code doesn't compile:
  // aFunctionImUsing(One); // Error unknown identifier One
  // But this did:
  struct Type
  {
     enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three };
  }
  static inline Lib1::LibEnum as(Type::MyType t) { return (Lib1::LibEnum)t; }

  // Now code like this compiles:
  aFunctionImUsing(as(Type::One));
  // This one doesn't:
  // aFunctionImUsing(Type::One); // Can't convert from Type::MyType to Lib1::LibEnum

  [...]
}

Решение 2:

namespace MyCode 
{
  struct Type
  {
     enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three };
  }

  // If you don't care about polluting your namespace with numerous wrapper 
  // you can write this instead of typedef someFunc:
  static inline void myFunc(Type::MyType t) { return Lib1::someFunc((Lib1::LibEnum)t); }

  // This one does:
  myFunc(Type::One); 
  [...]
}

Это 2 проблемы с фрагментом кода выше. Первая проблема заключается в том, что вы должны скопировать и вставить перечисление внутри вашего пространства имен (но с простым регулярным выражением в поиске и замене все готово). Вторая проблема заключается в том, что вашему пользователю придется использовать метод «как», что означает, что это не так просто, или вам нужно обернуть метод/функцию, используя второе решение.

В любом случае, поскольку внедрить перечисление в пространство имен невозможно, это решение — лучшее, что вы можете сделать. Обратите внимание, что ваш пользователь кода даже не знает, что вы используете библиотеку Lib1 в коде примера.

person X-Ryl669    schedule 11.10.2009

У меня была та же проблема, и я использовал это решение вместо того, чтобы возиться с лишними пространствами имен. Он надежно скрывает перечисление внутри класса и его явных пользователей.

class TreeWindow abstract {
public:
  enum CheckState { On, Off, Partial }
};

class TreeControl abstract {
public:
  class ItemChecked abstract: public TreeWindow { // this is the hack needed
  public:
    using TreeWindow::CheckState;
  };

  void func(ItemChecked::CheckState);
};

TreeControl& tree = ...
tree.func(TreeControl::ItemChecked::Partial);
person Jay    schedule 13.08.2011

Оказывается, простого решения нет. Меня тоже беспокоит. Если вам разрешено изменять A, существует решение, которое не зависит от пространств имен, если введение перечисления в область вне A нежелательно. В отличие от использования пространств имен, классы A и A_Enum могут быть вложенными.

struct A_Enum
{
    enum A_enum
    {
        E0,
        E1,
        E2
    };
};

struct A : public A_Enum
{
    using A_Enum::A_enum;
};

struct B : public::A_Enum
{
    using A_Enum::A_enum; // original enum name
    bool testA(A_enum val) { return (val == E1); }
};

EDIT2: снова удалил второе решение, не работает, как я думал.

person Gabriel Schreiber    schedule 16.01.2013

Почему у вас вообще есть тестовый метод в структуре B? Я не думаю, что это имеет смысл.

Определяя структуру A и определяя перечисление внутри нее, вы более или менее говорите, что «вот перечисление в области структуры A», и это то же самое, что сказать «если вы хотите использовать это перечисление, вы должны обратиться к структура А".

Помещение перечисления внутри пространства имен не решит вашу проблему. Потому что у вас будет такая же проблема с областью действия (C4482)

Мне кажется, вы слишком все усложняете. Что ты думаешь об этом?

struct A
{
    enum A_enum
    {
        E0,
        E1,
        E2
    };

    static bool test(A_enum val)
    {
        return (val == E1);
    }
};


int main()
{
    assert(A::test(A::E1));
    return 0;
}

Обратите внимание, что A::test() является статическим. Это должно быть потому, что вы не работаете с состоянием структуры. И поскольку он статичен, вам не нужен экземпляр A для вызова метода.

person ralphtheninja    schedule 23.05.2009
comment
Это пример. Реальный случай сложнее. Нет, я не могу так. - person rlbond; 23.05.2009