Вперед оголосивши перерахунок на C ++


263

Я намагаюся зробити щось подібне:

enum E;

void Foo(E e);

enum E {A, B, C};

який компілятор відкидає. Я швидко ознайомився з Google, і консенсус, здається, "ти не можеш цього зробити", але я не можу зрозуміти, чому. Хтось може пояснити?

Пояснення 2: Я роблю це, оскільки у мене є приватні методи в класі, які беруть згаданий перелік, і я не хочу, щоб значення перерахувань були піддані - так, наприклад, я не хочу, щоб хтось знав, що E визначено як

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

оскільки проект X - це не те, про що я хочу знати моїх користувачів.

Отже, я хотів переслати оголошення enum, щоб я міг помістити приватні методи у файл заголовка, оголосити enum внутрішньо у cpp та поширити вбудований бібліотечний файл та заголовок людям.

Щодо компілятора - це GCC.


Стільки років у цьому і якимось чином StackOverflow заманював мене назад;) Як післясмертна пропозиція - просто не робіть цього особливо в сценарії, який ви описуєте. Я вважаю за краще визначити абстрактний інтерфейс і викрити цей користувач, який зберігається, і зберегти визначення перерахунків та всі інші деталі реалізації з внутрішньою реалізацією, яку ніхто більше не бачить з моєї сторони, що дозволяє мені робити що завгодно і мати повний контроль над тим, коли користувачі бачать що завгодно.
RnR

Відповіді:


216

Причина, що перерахунок не може бути оголошений вперед, полягає в тому, що, не знаючи значень, компілятор не може знати сховище, необхідне для змінної enum. Компілятору C ++ дозволяється вказувати фактичний обсяг пам’яті виходячи з розміру, необхідного для вмісту всіх заданих значень. Якщо все, що видно, - це пряма декларація, блок перекладу не може знати, який розмір пам’яті буде обраний - це може бути знак char, int або щось інше.


З розділу 7.2.5 стандарту ISO C ++:

Базовий типом з перерахування є складовим типом , який може представляти всі значення перечіслітеля , певні в перерахуванні. Це визначено реалізацією, який інтегральний тип використовується як базовий тип для перерахування, за винятком того, що базовий тип не повинен бути більшим, ніж intякщо значення перелічувача не може вміститися в intабо unsigned int. Якщо список перелічувачів порожній, базовий тип є таким, як якщо б у перерахунку був один нумератор зі значенням 0. Значення, sizeof()застосоване до типу перерахування, об'єкта типу перерахування або перелічувача, є значенням, sizeof()застосованим до базовий тип.

Оскільки виклик функції повинен знати розміри параметрів, щоб правильно встановити стек виклику, перед прототипом функції повинно бути відомо число перерахувань у списку переліку.

Оновлення: у C ++ 0X запропонований та прийнятий синтаксис для випереджувального оголошення типів перерахунків. Ви можете ознайомитись із пропозицією на веб- сайті http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf


29
-1. Ваші міркування не можуть бути правильними - інакше, чому вам дозволяється переадресовувати "клас C;" а потім оголосити прототип функції, який приймає або повертає C, перш ніж повністю визначити C?
j_random_hacker

112
@j_random: Ви не можете використовувати клас до його повного визначення - ви можете використовувати лише вказівник або посилання на цей клас, і це тому, що їх розміри та способи операцій не залежать від класу.
RnR

27
Розмір посилання або вказівника на об’єкт класу встановлюється компілятором, і незалежно від фактичного розміру об'єкта - це розмір покажчиків та посилань. Перерахунок є об'єктом, і його розмір потрібен компілятору для доступу до правильного сховища.
KJAWolf

16
Логічно було б можливість оголосити покажчики / посилання на перерахунки, якби у нас були перекладені вперед перерахунки, як ми можемо робити з класами. Просто ти не часто маєш справу з покажчиками на перерахунків :)
Павло Мінаєв

20
Я знаю, що ця дискусія закінчилася давно, але я повинен вирівняти тут @j_random_hacker: проблема тут не в покажчику чи посиланні на неповні типи, а в використанні неповних типів у деклараціях. Оскільки це легально робити struct S; void foo(S s);(зауважте, що fooце лише декларовано, не визначено), то немає жодної причини, чому ми також не могли б це зробити enum E; void foo(E e);. В обох випадках розмір не потрібен.
Люк Турей

198

Попередня заява про перерахунки можлива з C ++ 11. Якщо раніше типи перерахунків не могли бути оголошені вперед, це те, що розмір перерахування залежить від його вмісту. Поки розмір перерахування визначається додатком, він може бути переданий вперед:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

1
Чи є підтримка компілятора для цієї функції? У GCC 4.5 його, здається, немає :(
rubenvb

4
@rubenvb Так само і Visual C ++ 11 (2012) blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
knatten

Я шукав enum32_t і з вашою відповіддю enum XXX: uint32_t {a, b, c};
фантастика

Я думав, що масштабні перерахунки (enum class) були реалізовані в C ++ 11? Якщо так, то як вони тоді легальні в C ++ 0X?
Терабіти

1
C ++ 0x була робочою назвою для C ++ 11 @Terrabits до того, як вона була офіційно стандартизована. Логіка полягає в тому, що якщо відомо, що функція (або велика ймовірність) включена до оновленого стандарту, то використання цієї функції до офіційного виходу стандарту має тенденцію використовувати робочу назву. (Наприклад, компілятори, які підтримували функції C ++ 11 до офіційної стандартизації в 2011 році, мали підтримку C ++ 0x; компілятори, які підтримували функції C ++ 17, до офіційної стандартизації мали підтримку C ++ 1z, і компілятори, що підтримують функції C ++ 20 прямо зараз (2019) матимуть підтримку C ++ 2a.)
Джастін Час -

79

Я додаю тут актуальну відповідь, враховуючи останні події.

Ви можете переслати оголошення перерахунку на C ++ 11, якщо ви одночасно оголосите його тип зберігання. Синтаксис виглядає приблизно так:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

Насправді, якщо функція ніколи не посилається на значення перерахування, то в цьому пункті вам взагалі не потрібно повне оголошення.

Це підтримується G ++ 4.6 і далі ( -std=c++0xабо -std=c++11в останніх версіях). Visual C ++ 2013 підтримує це; у попередніх версіях у нього є якась нестандартна підтримка, яку я ще не з'ясував - я знайшов припущення про те, що просте попереднє оголошення є законним, але YMMV.


4
+1, оскільки це єдина відповідь, в якій згадується, що вам потрібно оголосити тип у вашій декларації, а також своє визначення.
Туроні

Я вважаю, що часткова підтримка на початку MSVC підтримувалася від C ++ / CLI enum classяк розширення C ++ (до того, як C ++ 11 відрізнялася enum class), принаймні, якщо я правильно пам'ятаю. Компілятор дозволив вам вказати базовий тип enum, але не підтримував і не enum classоголосив передумови, і попередив вас, що кваліфікувати перелічувача з областю enum є нестандартним розширенням. Я пам’ятаю, що вона працює приблизно так само, як і вказати базовий тип у C ++ 11, за винятком більш дратівливих, оскільки вам довелося придушити попередження.
Час Джастіна -

30

Переслати оголошення в C ++ дуже корисно, оскільки це значно прискорює час компіляції . Ви можете оголосити вперед кілька речей , в C ++ , включаючи: struct, class, function, і т.д. ...

Але чи можете ви переслати оголошення enumв C ++?

Ні, ти не можеш.

Але чому б цього не допустити? Якщо це було дозволено, ви можете визначити enumтип у файлі заголовка та enumзначення у вихідному файлі. Здається, це слід дозволити правда?

Неправильно.

У C ++ відсутній тип за замовчуванням, enumяк, наприклад, у C # (int). У C ++ ваш enumкомпілятор визначатиме тип, який буде відповідним діапазону значень для вашого enum.

Що це означає?

Це означає, що ваш enumбазовий тип не може бути повністю визначений, поки у вас не будуть визначені всі значення enum. Які люди не можете розділити декларацію та визначення свого enum. І тому ви не можете переслати оголошення enumв C ++.

Стандарт ISO C ++ S7.2.5:

Основний тип перерахунку - це інтегральний тип, який може представляти всі значення перерахунку, визначені в перерахунку. Це визначено реалізацією, який інтегральний тип використовується як базовий тип для перерахування, за винятком того, що базовий тип не повинен бути більшим, ніж intякщо значення перелічувача не може вміститися в intабо unsigned int. Якщо список перелічувачів порожній, базовий тип є таким, як якщо б у перерахунку був один нумератор зі значенням 0. Значення, sizeof()застосоване до типу перерахування, об'єкта типу перерахування або перелічувача, є значенням, sizeof()застосованим до базовий тип.

Ви можете визначити розмір переліченого типу в C ++ за допомогою sizeofоператора. Розмір переліченого типу - це розмір його основного типу. Таким чином ви можете здогадатися, який тип використовує ваш компілятор enum.

Що робити, якщо ви enumпрямо вказали тип свого подібного:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Чи можете ви потім переслати заявити про своє enum?

Ні. Але чому б і ні?

Вказання типу анкет enumфактично не є частиною поточного стандарту C ++. Це розширення VC ++. Він буде частиною C ++ 0x, хоча.

Джерело


14
Зараз ця відповідь застаріла на кілька років.
Том

Час робить усіх дурнів усіх нас. Ваш коментар застарів уже кілька років; відповідь через десять років!
pjcard

14

[Моя відповідь неправильна, але я залишив її тут, оскільки коментарі корисні].

Передача оголошень перерахунків нестандартна, тому що покажчики на різні типи перерахунків не гарантуються однакового розміру. Компілятору може знадобитися означення, щоб знати, які покажчики розмірів можуть використовуватися для цього типу.

На практиці принаймні на всіх популярних компіляторах покажчики на перерахунки мають однаковий розмір. Передача декларацій перерахунків надається, наприклад, як розширення мови Visual C ++.


2
-1. Якщо ваше міркування було правильним, те саме міркування означало б, що пересилання оголошень типів класів не можна використовувати для створення покажчиків на ці типи - але вони можуть.
j_random_hacker

6
+1. Обґрунтування правильне. Конкретний випадок - платформи, де sizeof (char *)> sizeof (int *). Обидва можуть бути основними типами для перерахунку, залежно від діапазону. Класи не мають основних типів, тому аналогія помилкова.
MSalters

3
@MSalters: Приклад: "struct S {int x;};" Тепер sizeof (S *) повинен дорівнювати розміру будь-якого іншого вказівника на структуру, оскільки C ++ дозволяє оголосити та використати такий покажчик до визначення S ...
j_random_hacker

1
@MSalters: ... На платформі, де sizeof (char *)> sizeof (int *), використання такого "повнорозмірного" покажчика для цієї конкретної структури може бути неефективним, але це значно спрощує кодування - і точно таке ж щось можна і потрібно зробити для переліків.
j_random_hacker

4
покажчики даних і покажчики на функції можуть бути різного розміру, але я досить впевнений, що вказівники даних мають бути в зворотному напрямку (кинутий на інший тип вказівника даних, потім повернутися до оригіналу, потрібно ще працювати), що означає, що всі покажчики даних однакового розміру.
Бен Войгт

7

Дійсно немає такого поняття, як пряма заява про перерахування. Оскільки визначення enum не містить жодного коду, який міг би залежати від іншого коду, що використовує enum, зазвичай не проблема повністю визначити enum, коли ви вперше заявляєте про це.

Якщо ваш enum використовується лише приватними функціями-членами, ви можете реалізувати інкапсуляцію, маючи сам enum приватним членом цього класу. Перерахунок ще має бути повністю визначений у точці декларування, тобто у межах визначення класу. Однак це не є більшою проблемою, оскільки декларування функцій приватних членів там, і не є гіршим розкриттям внутрішніх питань реалізації, ніж це.

Якщо вам потрібна глибша ступінь прихованості для деталей вашої реалізації, ви можете розбити її на абстрактний інтерфейс, що складається лише з чистих віртуальних функцій та конкретного, повністю прихованого, класу, що реалізує (успадковує) інтерфейс. Створенням екземплярів класу може займатися фабрична або статична функція члена інтерфейсу. Таким чином, навіть справжнє ім’я класу, не кажучи вже про його приватні функції, не виявиться.


5

Відзначимо, що причина насправді полягає в тому, що розмір перерахунку ще не відомий після прямого оголошення. Ну, ви використовуєте пряме декларування структури, щоб мати змогу передавати вказівник навколо або звертатися до об'єкта з місця, на яке посилається і в самому визначенні вперед оголошеної структури.

Наперед оголосити перерахунок було б не надто корисно, тому що хотілося б, щоб можна було обміняти значення перерахунку. Ви навіть не можете мати вказівник на нього, тому що нещодавно мені сказали, що деякі платформи використовують покажчики іншого розміру для char, ніж для int або long. Тож все залежить від змісту перерахунку.

Поточний стандарт C ++ явно забороняє робити щось подібне

enum X;

7.1.5.3/1). Але на наступному стандарт C ++ з - за наступний рік дозволяє наступне, що переконало мене проблему на самому справі має справу з базовим типом:

enum X : int;

Це відоме як "непрозора" декларація перерахунку. Ви навіть можете використовувати X за значенням у наступному коді. А його перелічувачі можуть бути визначені пізніше передекларацією перерахування. Дивіться 7.2у поточному робочому проекті.


4

Я зробив би це так:

[у загальнодоступному заголовку]

typedef unsigned long E;

void Foo(E e);

[у внутрішньому заголовку]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

Додаючи FORCE_32BIT, ми гарантуємо, що Econtent збирається на тривалий час, тому він взаємозамінний з E.


1
Звичайно, це означає, що (A) типи E та Econtent відрізняються, а (B) для систем LP64, sizeof (E) = 2 * sizeof (EContent). Тривіальне виправлення: ULONG_MAX, також простіше для читання.
MSalters

2

Якщо ви дійсно не хочете, щоб ваш enum відображався у вашому заголовковому файлі, і гарантували, що він використовується лише приватними методами, тоді можна вирішити одне рішення з принципом pimpl.

Це техніка, яка забезпечує приховання внутрішніх класів у заголовках, просто оголосивши:

class A 
{
public:
    ...
private:
    void* pImpl;
};

Потім у вашому файлі реалізації (cpp) ви оголошуєте клас, який буде представляти внутрішні.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

Ви повинні динамічно створювати реалізацію в конструкторі класів і видаляти її в деструкторі, і при реалізації відкритого методу необхідно використовувати:

((AImpl*)pImpl)->PrivateMethod();

Існують плюси використання pimpl, один полягає в тому, що він відокремлює заголовок класу від його реалізації, не потрібно перекомпілювати інші класи при зміні однієї реалізації класу. Інша справа - це пришвидшення вашого часу на компіляцію, оскільки заголовки такі прості.

Але це боляче, тож вам слід справді запитати себе, чи не просто так оголосити свою перерахунку як приватну в заголовку.


3
struct AImpl; struct A {private: AImpl * pImpl; };

2

Ви можете загортати перерахунок у структуру, додаючи в деякі конструктори та вводити перетворення, а натомість оголошувати структуру замість цього.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

Схоже, це працює: http://ideone.com/TYtP2



1

Є деякі інакомислення, оскільки це натрапило (на зразок), ось ось деякі відповідні біти від стандарту. Дослідження показують, що стандарт насправді не визначає пряму декларацію, а також чітко не зазначає, що перерахунки можуть або не можуть бути оголошені вперед.

По-перше, з dcl.enum, розділ 7.2:

Основний тип перерахунку - це інтегральний тип, який може представляти всі значення перерахунку, визначені в перерахунку. Визначено реалізацією, який інтегральний тип використовується як базовий тип для перерахування, за винятком того, що базовий тип не повинен бути більшим за int, якщо значення перелічувача не може вміститися в int або неподписаний int. Якщо список перелічувачів порожній, базовий тип є таким, як якщо б перерахунок мав один нумератор зі значенням 0. Значення sizeof (), застосоване до типу перерахування, об'єкта типу перерахування або перелічувача, є значенням sizeof (), застосований до базового типу.

Отже, базовий тип перерахунку визначається реалізацією, з одним незначним обмеженням.

Далі ми переходимо до розділу про "неповні типи" (3.9), який приблизно наближається до того, що ми підходимо до будь-якого стандарту про прямі декларації:

Клас, який був оголошений, але не визначений, або масив невідомого розміру або неповний тип елемента - це неповно визначений тип об'єкта.

Тип класу (наприклад, "клас X") може бути неповним в одній точці одиниці перекладу і завершеним пізніше; тип "клас X" - це один і той же тип в обох точках. Оголошений тип об’єкта масиву може бути масивом неповного типу класу і, отже, неповним; якщо тип класу буде завершено пізніше в блоці перекладу, тип масиву стає завершеним; тип масиву в цих двох точках - це один і той же тип. Заявлений тип об’єкта масиву може бути масивом невідомого розміру і тому бути неповним в одній точці одиниці перекладу та завершеним пізніше; типи масивів у цих двох точках ("масив невідомої межі T" і "масив N T") є різними типами. Тип вказівника на масив невідомого розміру або типу, визначеного декларацією typedef, щоб бути масивом невідомого розміру,

Так що там стандарт майже виклав типи, які можна оголосити вперед. Енума там не було, тому автори компілятора, як правило, вважають вперед оголошення, заборонені стандартом через змінні розміри його базового типу.

Це має сенс теж. На переліків зазвичай посилаються в ситуаціях за вартістю, і компілятору дійсно потрібно знати розмір пам’яті в цих ситуаціях. Оскільки розмір сховища визначено реалізацією, багато компіляторів можуть просто обрати 32-бітні значення для базового типу кожного перерахунку, і в цей момент стає можливим переслати їх оголошення. Цікавим експериментом може бути спробу вперед оголосити перерахунок у візуальній студії, а потім змусити його використовувати базовий тип, більший за sizeof (int), як пояснено вище, щоб побачити, що відбувається.


зауважте, що це прямо забороняє "enum foo;" в 7.1.5.3/1 (але як і у всьому, доки компілятор попереджає, він, звичайно, може все-таки зібрати такий код)
Йоханнес Шауб - ліб

Дякую, що вказали на це, це дійсно езотеричний абзац, і це може зайняти тиждень, щоб проаналізувати його. Але добре знати, що там є.
Ден Олсон

Не хвилюйтесь. Деякі стандартні абзаци справді дивні :) ну, розроблений специфікатор типу - це те, де ви вказуєте тип, але також вказуєте щось більше, щоб зробити його однозначним. наприклад, "struct X" замість "X", або "enum Y", а не "Y". Вам це потрібно, щоб стверджувати, що це справді тип.
Йоханнес Шауб - ліб

тож ви можете використовувати його так: "клас X * foo;" якщо X ще не був оголошений вперед або "typename X :: foo" в шаблоні для розрізнення. або "клас посилання obj;" якщо в тій же області є функція "link", яка б затінювала клас, що має те саме ім'я.
Йоханнес Шауб - ліб

в 3.4.4 сказано, що вони використовуються, якщо якесь нетипове ім'я приховує ім'я типу. саме там вони найчастіше використовуються, окрім того, що вперед декларується як "клас X;" (тут вона є єдиною складовою декларації). тут йдеться про них у нешаблонах. проте 14.6 / 3 перераховує використання їх у шаблонах.
Йоханнес Шауб - ліб

1

Для VC ось тест щодо прямого декларування та визначення базового типу:

  1. наступний код складається добре.
    typedef int myint;
    перерахунок Т;
    пустоту (T * tp)
    {
        * tp = (T) 0x12345678;
    }
    перерахунок T: char
    {
        А
    };

Але я отримав попередження для / W4 (/ W3 не несе цього попередження)

попередження C4480: нестандартне розширення, що використовується: визначення базового типу для enum 'T'

  1. 32-розрядний C / C ++ оптимізаційний компілятор VC (Microsoft (R) для версії 15.00.30729.01 для 80x86) виглядає непогашеним у наведеному вище випадку:

    • коли бачимо перерахунок T; VC передбачає, що тип enum T використовує типовий 4 байт int як базовий тип, тож сформований код складання:
    ? foo @@ YAXPAW4T @@@ Z PROC; foo
    ; Файл e: \ work \ c_cpp \ cpp_snippet.cpp
    ; Рядок 13
        поштовх
        mov ebp, esp
    ; Рядок 14
        mov eax, DWORD PTR _tp $ [ebp]
        mov DWORD PTR [eax], 305419896; 12345678H
    ; Рядок 15
        pop ebp
        ret 0
    ? foo @@ YAXPAW4T @@@ Z ENDP; foo

Вищевказаний код складання витягується безпосередньо з /Fatest.asm, а не моя особиста здогадка. Чи бачите ви mov DWORD PTR [eax], 305419896; 12345678H лінія?

наступний фрагмент коду доводить це:

    int main (int argc, char * argv)
    {
        союз {
            char ca [4];
            Т т;
        } а;
        a.ca [0] = a.ca [1] = a. [ca [2] = a.ca [3] = 1;
        foo (& a.t);
        printf ("% # x,% # x,% # x,% # x \ n", a.ca [0], a.ca [1], a.ca [2], a.ca [3]) ;
        повернути 0;
    }

результат: 0x78, 0x56, 0x34, 0x12

  • після видалення прямого оголошення enum T і перемістіть визначення функції foo після визначення enum T: результат ОК:

наведена вище ключова інструкція стає:

mov BYTE PTR [eax], 120; 00000078H

кінцевий результат: 0x78, 0x1, 0x1, 0x1

Зверніть увагу, що значення не перезаписується

Тож використання попереднього оголошення перерахунку у ВК вважається шкідливим.

BTW, щоб не дивувати, синтаксис для декларування базового типу такий же, як його у C #. У практиці я виявив, що варто зберегти 3 байти, вказавши базовий тип char, коли говорити з вбудованою системою, яка обмежена пам'яттю.


1

У своїх проектах я застосував техніку перерахування простору імен для вирішення проблем із enumзастарілих та сторонніх компонентів. Ось приклад:

вперед.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

Зауважте, що foo.hзаголовок нічого не повинен знати legacy::evil. legacy::evilНеобхідно включати лише ті файли, які використовують старий тип (тут: main.cc) enum.h.


0

Моє рішення вашої проблеми було б:

1 - використовуйте int замість enums: Заявіть про свої ints у анонімному просторі імен у файлі CPP (а не у заголовку):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

Оскільки ваші методи приватні, ніхто не буде возитися з даними. Ви навіть можете піти далі перевірити, чи хтось надсилає вам недійсні дані:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2: створити повний клас з обмеженими моментами const, як це робиться в Java. Вперед оголосити клас, а потім визначити його у файлі CPP та інстанціювати лише значення, подібні до перерахунків. Я щось подібне робив у C ++, і результат був не таким задоволеним, як бажано, тому що йому потрібен був якийсь код для імітації перерахунку (конструкція копії, оператор = тощо).

3: Як було запропоновано раніше, використовуйте приватно оголошений перелік. Незважаючи на те, що користувач побачить його повне визначення, він не зможе ним користуватися, а також не використовувати приватні методи. Таким чином, ви зазвичай зможете змінювати перелік та вміст існуючих методів, не потребуючи перекомпіляції коду за допомогою свого класу.

Моя здогадка буде або рішенням 3, або 1.


-1

Оскільки enum може бути інтегральним розміром різного розміру (компілятор вирішує, який розмір має певний enum), вказівник на enum також може мати різний розмір, оскільки це інтегральний тип (символи мають покажчики різного розміру на деяких платформах наприклад).

Тож компілятор навіть не може дозволити вам передати оголошення enum та користувачеві вказівник на нього, бо навіть там він потребує розміру enum.


-1

Ви визначаєте перерахунок для обмеження можливих значень елементів типу обмеженим набором. Це обмеження має застосовуватися під час компіляції.

Якщо вперед оголосити той факт, що згодом ви будете використовувати "обмежений набір", не додає жодного значення: наступний код повинен знати можливі значення, щоб отримати користь.

Незважаючи на те, що компілятор буде стурбований розміром перераховується типу, то мета перерахування втрачається , коли ви вперед оголосити.


1
Ні, наступний код не повинен знати значення, щоб це було корисно - зокрема, якщо наступний код є лише прототипом функції, що приймає або повертає параметри перерахунку, розмір типу не важливий. Використання прямого декларування тут може усунути залежності побудови, прискоривши компіляцію.
j_random_hacker

Ти маєш рацію. Намір полягає не в підпорядкуванні значень, а в типі. Вирішено з 0x типами Enum.
xtofl
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.