Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Целочисленная константа в классе - enum или static const?

#cpp #cpp_faq


Есть два способа сделать константное значение в классе:

Использовать перечисление:

class C {
  enum { X = 42; }
};


Или статический член класса:

class C {
  static const auto X = 42;
};


Какой способ предпочтительнее? Какие могут быть подводные камни?
    


Ответы

Ответ 1



Статический член класса позволяет использовать вывод типов: static const auto X = 1ul; Для enum тип надо писать самому (если он не int): enum { X = 1 }; enum : unsigned long { Y = 1 }; Получается что статический член класса выглядит предпочтительнее, к тому же он семантически является константой, а перечисление больше рассчитано на "перечисление" нескольких значений. Подводные камни Если мы попробуем скомпилировать следующий код struct C { static const auto X = 1; }; int f(const int& arg) { return arg; } int main() { return f(C::X); } То мы можем получить ошибку линковки (при отключенном оптимизаторе): main.cpp:(.text+0x15): undefined reference to `C::X' Оказывается, статические целочисленные константные члены классов не требуют отдельного определения только если они не ODR-использованы (odr-used). Мы берем ссылку на C::X, это делает ее odr-used, и значит мы должны добавить ее определение вне класса: struct C { static const auto X = 1; }; const int C::X; // В одном из .cpp файлов. Термин "odr-used" определен в главе "One definition rule [basic.def.odr]" следующим образом: Переменная, чье имя использовано в потенциально вычисляемом контексте (т.е. не sizeof или decltype), является odr-used, за исключением случая когда она является объектом, который может быть использован в константном выражении, и к ней сразу же применяется преобразование из lvalue в rvalue. В нашем случае C::X может быть использована в константном выражении, однако она является lvalue и передается в f() как ссылка на lvalue. Если мы добавим преобразование в rvalue, то отдельное определение C::X уже будет не нужно, и ошибка линковки пропадет: f(static_cast(C::X)); // явный каст f(+C::X); // унарный плюс Какой из этого можно сделать вывод? Статические арифметические константные члены классов слишком удобны чтобы от них отказываться, и если вдруг появится ошибка линковки, то достаточно использовать унарный плюс чтобы ее починить.

Ответ 2



Если нужен ограниченный набор констант для определенного действия то в приоритете перечисление (enum) для примера приведу перичисление цвета: enum color { red; green; blue; }; т.е. количество принимающих значений для цвета конечно. во всех остальных случаях нужно применять обычную константу

Комментариев нет:

Отправить комментарий