Есть ли способ использовать ключевое слово как идентификатор в перечислении?
Я не смог найти, есть ли способ использовать ключевое слово в определении перечисления, например:
enum class EServerAction
{
create,
read,
update,
delete
};
В С# я могу использовать @ char, чтобы компилятор рассматривал его как идентификатор. Есть ли способ сделать это в С++ (Visual Studio 2015)?
Ответы
Ответ 1
Нет, они не могут использоваться.
Из MSDN
Ключевые слова - это предопределенные зарезервированные идентификаторы, которые имеют специальные значения. Они не могут использоваться как идентификаторы в вашей программе.
В правиле для идентификатора указано:
Идентификатор может использоваться для обозначения объектов, ссылок, функций, перечисления, типы, члены класса, пространства имен, шаблоны, шаблон специализации, пакеты параметров, метки goto и другие объекты, со следующими исключениями:
- идентификаторы, которые являются ключевыми словами, не могут использоваться для других целей;
- идентификаторы с двойным подчеркиванием в любом месте зарезервированы;
- зарезервированы идентификаторы, начинающиеся с символа подчеркивания, за которым следует буква верхнего регистра;
- идентификаторы, начинающиеся с символа подчеркивания, зарезервированы в глобальном пространстве имен.
Ответ 2
Согласно 2.12 [lex.key] в стандарте С++ 14, в качестве идентификаторов должны использоваться некие идентификаторы :
Идентификаторы, показанные в таблице 4, зарезервированы для использования в качестве ключевых слов (то есть они безоговорочно рассматриваются как ключевые слова в фазе 7), за исключением маркера атрибута (7.6.1) [Примечание: ключевое слово export не используется, но зарезервировано для будущего использования. - конец примечания]:
Таблица 4 - Ключевые слова
alignas continue friend register true
alignof decltype goto reinterpret_cast try
asm default if return typedef
auto delete inline short typeid
bool do int signed typename
break double long sizeof union
case dynamic_cast mutable static unsigned
catch else namespace static_assert using
char enum new static_cast virtual
char16_t explicit noexcept struct void
char32_t export nullptr switch volatile
class extern operator template wchar_t
const false private this while
constexpr float protected thread_local
const_cast for public throw
Кроме того, некоторые идентификаторы не должны использоваться:
Кроме того, альтернативные представления, показанные в таблице 5 для некоторых операторов и пунктуаторов (2.6), зарезервированы и не должны использоваться иначе:
Таблица 5 - Альтернативные представления
and and_eq bitand bitor compl not
not_eq or or_eq xor xor_eq
Более того, согласно 2.11 Identifier [lex.name], некоторые из них незаконны для использования, но компилятор не обязан сообщать вам:
некоторые идентификаторы зарезервированы для использования реализацией С++ и стандартными библиотеками (17.6.4.3.2) и не должны использоваться иначе; диагностика не требуется
- Каждое имя, содержащее двойное подчеркивание _ _ или начинающееся с символа подчеркивания, за которым следует заглавная буква (2.12), зарезервировано для реализации для любого использования.
- Каждое имя, начинающееся с символа подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.
Ответ 3
В С++ ключевые слова не могут использоваться как идентификаторы, как они могут в С#.
Ответ 4
Это можно сделать с помощью макросов:
#define delete _delete
enum class EServerAction
{
create,
read,
update,
delete
};
Эта практика обычно обескуражена, потому что теперь вы не можете использовать delete
в других частях файла. Но это полезно в некоторых ситуациях, например, при компиляции программы на языке C, которая имеет идентификаторы, называемые ключевыми словами С++ (например, delete
), используя компилятор С++.
Это также вызовет путаницу в отладчике, потому что идентификаторы для этих символов не совпадают с идентификаторами, которые находятся в исходном коде.
Ответ 5
Использовать MSVC __identifier: https://docs.microsoft.com/en-us/cpp/extensions/identifier-cpp-cli?view=vs-2019
enum class EServerAction
{
create,
read,
update,
__identifier(delete)
};
Ответ 6
РЕДАКТИРОВАТЬ
Краткий ответ: НЕТ!
В качестве идентификаторов вы можете использовать только контекстные ключевые слова, такие как override
. Но ты не должен. Стандарт допускает это только для обратной совместимости, которая может быть удалена или устарела. А также заставить его работать для каждого ключевого слова означало бы нарушить многие другие семантики языка.
Подумайте об альтернативном способе именования записей enum.
КОНЕЦ РЕДАКТИРОВАНИЯ
НОТА
Я попытался сделать это с помощью C-препроцессора. Следующие примеры кода не являются ни хорошей практикой, ни стандартом. Следующие примеры кода должны демонстрировать только поведение C-препроцессора. Использование этих методов может вызвать проблемы, особенно при использовании библиотечных заголовков, потому что переопределение этих ключевых слов может изменить семантику любого кода.
КОНЕЦ ПРИМЕЧАНИЕ
С некоторыми именами это можно сделать с помощью макросов, здесь все ключевые слова сделаны пригодными для использования в качестве обычных идентификаторов без потери значения самих ключевых слов. Во-первых, сохраняя их в константу или определяя тип с помощью допустимых идентификаторов, таких как nokw_true
или nokw_bool
. Затем определяем макросы с ключевым словом name для ссылки на псевдонимы.
constexpr static nullptr_t nokw_nullptr = nullptr;
constexpr static bool nokw_true = true;
constexpr static bool nokw_false = false;
using nokw_bool = bool;
using nokw_void = void; // suprising: this can also be used for empty parameter list
#define nullptr nokw_nullptr
#define true nokw_true
#define false nokw_false
#define bool nokw_bool
#define void nokw_void
НОТА
Я протестировал этот код, и он работал так, как будто макросы никогда не были определены. Но нет никакой гарантии, что он ведет себя одинаково на каждом компиляторе C++, потому что это не одно и то же, ключевые слова сильно отличаются от идентификаторов. И он не совместим напрямую с более новыми стандартами C++, потому что они могут добавить новый синтаксис, используя эти ключевые слова, что будет недопустимо с идентификаторами под капотом.
КОНЕЦ ПРИМЕЧАНИЕ
С другими фундаментальными типами, такими как int
или char
больше невозможно было бы использовать их как unsigned long int
или signed char
когда они переопределены с using
/typedef
. Типы whar_t
, char16_t
и char32_t
большую часть времени объявлены typedef и поэтому не являются "реальным" ключевым словом, но если ваш компилятор реализует его непосредственно как ключевое слово, вы можете сделать его using nokw_* = *;
использования в качестве идентификатора с using nokw_* = *;
подход.
ПРИМЕЧАНИЕ Только не делайте этого! КОНЕЦ ПРИМЕЧАНИЕ
*_cast
-keywords также может быть сделан как не -keyword, определяя их в функции:
template<typename N, typename O>
N nokw_reinterpret_cast(O&& old) { return reinterpret_cast<N>(old); };
#define reinterpret_cast nokw_reinterpret_cast
// other cast-operators follow
Другие ключевые слова не могут быть определены, потому что они не являются ни типовыми, ни функциональными ключевыми словами в качестве операторов приведения. В вашем случае delete
-keyword не может существовать с идентификатором одновременно, потому что удаление имеет особое синтаксическое значение. Подумайте об delete ptr
или удаленных функций.
Но используя определения выше, можно реализовать tribool
с ключевыми словами true
и false
в качестве enum. Здесь важен class
-keyword, потому что имена могут конфликтовать с ранее определенными именами/использование идентификаторов неоднозначно.
enum class tribool { true, maybe, false };
Или это также может быть в пространстве имен:
// maybe writing an API for a Visual Basic Application
namespace vb_constants {
constexpr int true = -1; // in VB True is -1
constexpr int false = 0;
// in this namespace the real true and the real false can
// be accessed through ::true and ::false
};
Записи enum могут быть использованы как tribool::true
.
ОДНАКО: Многие программисты используют CAPITALIZED enum-entry-names. Поскольку нет заглавных слов, они всегда будут работать...
НОТА
Это не является нарушением стандарта C++ или стандарта C. По этой же причине часто рекомендуется использовать CAPITALIZED macronames. Макросы видны глобально для всех файлов, включенных после определения, другие объекты могут и должны быть вложены в пространства имен. В противном случае это может привести к конфликту имен. Когда вам нужно определить что-то как макрос, вы должны добавить к макронайму какой-то уникальный текст, например имя вашей библиотеки/приложения, как вы бы назвали пространство имен C++. Это также называлось пространствами имен, прежде чем C++ даже имел пространства имен. Посмотрите на OpenGL, например, все идентификаторы начинаются с GL
, это работает так же хорошо, как пространства имен C++, но он не позволяет конструкциям using ns::the_class
или using namespace
, которое, однако, вы никогда не должны использовать (глобально).
КОНЕЦ ПРИМЕЧАНИЕ
Я бы пошел с
enum class EServerAction {
CREATE,
READ,
UPDATE,
DELETE
};
НОТА
Когда я писал это, я пытался сохранить всю семантику ключевых слов; но даже трогать ключевые слова, переопределяя их как макросы, очень плохо, потому что это запрещено стандартом. Возможно, вы никогда не узнаете, как библиотеки/стандартные реализации библиотек/конкретные системные заголовки используют или могут их переопределить.
КОНЕЦ ПРИМЕЧАНИЕ