c ++ typedef перечисление другого класса? - PullRequest
12 голосов
/ 23 мая 2009

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

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, я получу предупреждение, что оно нестандартное. Есть ли хороший способ сделать что-то подобное?

Ответы [ 6 ]

3 голосов
/ 23 мая 2009

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

2 голосов
/ 23 мая 2009

Помещение enum в глобальную область слишком уязвимо, а помещение его в класс может привести к нежелательной зависимости. Чтобы 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)

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

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

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

myEnum_e val = myEnum::horizontal;

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

0 голосов
/ 16 января 2013

Кажется, нет простого решения. Меня тоже беспокоит. Если вам разрешено изменять A, существует решение, которое не зависит от пространств имен, если нежелательно вводить enum в область действия за пределами 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: снова удалено второе решение, не работает, как я думал.

0 голосов
/ 13 августа 2011

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

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);
0 голосов
/ 11 октября 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 проблемы с фрагментом кода выше. Первая проблема заключается в том, что вы должны скопировать и вставить enum в ваше пространство имен (но с помощью простого регулярного выражения в find & replace, все готово). Вторая проблема заключается в том, что ваш пользователь должен будет использовать метод «как», что означает, что он не прост, или вы должны обернуть метод / функцию, используя второе решение.

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

0 голосов
/ 23 мая 2009

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

Определяя структуру A и определяя перечисление внутри нее, вы более или менее говорите, что «вот перечисление в области действия структуры 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 для вызова метода.

...