有没有办法在枚举中使用关键字作为标识符?
Is there a way to use a keyword as identifier in an enum?
我一直无法找到是否可以在枚举定义中使用关键字,例如:
enum class EServerAction
{
create,
read,
update,
delete
};
在 C# 中,我可以使用 @ 字符让编译器将其视为标识符。有没有办法在 C++ 中执行此操作(Visual Studio 2015)?
不,它们不能使用。
来自MSDN
Keywords are predefined reserved identifiers that have special
meanings. They cannot be used as identifiers in your program.
标识符的 rule 表示:
An identifier can be used to name objects, references, functions,
enumerators, types, class members, namespaces, templates, template
specializations, parameter packs, goto labels, and other entities,
with the following exceptions:
- the identifiers that are keywords cannot be used for other purposes;
- the identifiers with a double underscore anywhere are reserved;
- the identifiers that begin with an underscore followed by an uppercase letter are reserved;
- the identifiers that begin with an underscore are reserved in the global namespace.
在 C++ 中,关键字不能像在 C# 中那样用作标识符。
可以使用宏来做到这一点:
#define delete _delete
enum class EServerAction
{
create,
read,
update,
delete
};
通常不鼓励这种做法,因为现在您不能在文件的其他部分使用 delete
。但它在某些情况下很有用,例如使用 C++ 编译器编译具有名为 C++ 关键字(如 delete
)的标识符的 C 程序时。
这也会在调试器中造成混淆,因为符号的标识符与源代码中的标识符不同。
根据 C++14 标准中的 2.12 [lex.key],某些标识符 绝不能 用作标识符:
The identifiers shown in Table 4 are reserved for use as keywords (that is, they are unconditionally treated as keywords in phase 7) except in an attribute token (7.6.1) [ Note: The export keyword is unused but is reserved for future use. — end note ]:
Table 4 — Keywords
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
此外,一些标识符不得使用:
Furthermore, the alternative representations shown in Table 5 for certain operators and punctuators (2.6) are reserved and shall not be used otherwise:
Table 5 — Alternative representations
and and_eq bitand bitor compl not
not_eq or or_eq xor xor_eq
此外,根据 2.11 标识符 [lex.name],有些 非法使用 ,但编译器不需要告诉您:
some identifiers are reserved for use by C++ implementations and standard libraries (17.6.4.3.2) and shall not be used otherwise; no diagnostic is required
— Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
— Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
编辑
简答:否!
您只能使用 override
等上下文关键字作为标识符。但你不应该。该标准仅允许向后兼容,这可能会被删除或弃用。并且让它适用于每个关键字将意味着破坏该语言的许多其他语义。
考虑另一种命名枚举条目的方法。
结束编辑
注意
我试图通过使用 C-preprocessor 来实现它。
以下代码示例既不是良好做法,也不是标准所允许的。以下代码示例应仅演示 C-preprocessor 的行为。使用这些技术可能会导致问题,尤其是在使用库 headers 时,因为重新定义这些关键字会改变任何代码的语义。
尾注
对于某些名称,可以使用宏来执行此操作,这里所有关键字都可用作普通标识符,而不会丢失关键字本身的含义。首先,将它们存储到常量中或使用 nokw_true
或 nokw_bool
等有效标识符对类型进行类型定义。然后用 keyword-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
等其他基本类型,当使用 [=22= 重新声明时,将无法再使用它们,例如 unsigned long int
或 signed char
]/typedef
。类型 whar_t
、char16_t
和 char32_t
大多数时候由 typedef 声明,因此不是 "real" 关键字,但如果您的编译器直接将其实现为关键字,您可以使用 using nokw_* = *;
方法将其用作标识符。
注意 千万不要这样做! 尾注
*_cast
-关键字也可以作为 non-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
无法定义其他关键字,因为它们既不是类型,也不是像 cast-operators 那样的 function-like 关键字。在您的情况下,delete
-关键字不能与标识符同时存在,因为 delete 具有特殊的句法含义。考虑 delete ptr
或删除函数。
但是使用上面的定义可以实现带有关键字 true
和 false
的 tribool
作为枚举。 class
-关键字在这里很重要,因为名称可能与先前定义的名称冲突names/the 标识符的使用不明确。
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-entries可以像tribool::true
一样使用。
然而:许多程序员使用 CAPITALIZED enum-entry-names。因为没有大写关键字,所以这些将始终有效...
注意
这既不违反 C++ 标准也不违反 C-standard。这也是为什么经常建议使用大写宏名的原因。宏对定义后包含的所有文件全局可见,其他实体可以并且应该嵌套在命名空间中。否则可能会导致名称冲突。当您需要将某些东西定义为宏时,您应该在宏名称前添加一些独特的文本,例如您的 library/app 名称,就像您命名 C++ 名称空间一样。这也被称为命名空间,在 C++ 甚至有命名空间之前。以 OpenGL 为例,所有标识符都以 GL
为前缀,这与 C++ 名称空间一样有效,但它不允许构造为 using ns::the_class
或 using namespace
,但是你永远不应该使用它们(全球)。
尾注
我会选择
enum class EServerAction {
CREATE,
READ,
UPDATE,
DELETE
};
注意
当我写这篇文章时,我试图保留关键字的所有语义;但即使通过将关键字重新定义为宏来触及关键字也是非常糟糕的,因为标准禁止这样做。您可能永远不知道 libraries/standard 库 implementations/specific 系统 headers 如何使用或可能重新定义它们。
尾注
使用 MSVC __identifier: https://docs.microsoft.com/en-us/cpp/extensions/identifier-cpp-cli?view=vs-2019
enum class EServerAction
{
create,
read,
update,
__identifier(delete)
};
我一直无法找到是否可以在枚举定义中使用关键字,例如:
enum class EServerAction
{
create,
read,
update,
delete
};
在 C# 中,我可以使用 @ 字符让编译器将其视为标识符。有没有办法在 C++ 中执行此操作(Visual Studio 2015)?
不,它们不能使用。
来自MSDN
Keywords are predefined reserved identifiers that have special meanings. They cannot be used as identifiers in your program.
标识符的 rule 表示:
An identifier can be used to name objects, references, functions, enumerators, types, class members, namespaces, templates, template specializations, parameter packs, goto labels, and other entities, with the following exceptions:
- the identifiers that are keywords cannot be used for other purposes;
- the identifiers with a double underscore anywhere are reserved;
- the identifiers that begin with an underscore followed by an uppercase letter are reserved;
- the identifiers that begin with an underscore are reserved in the global namespace.
在 C++ 中,关键字不能像在 C# 中那样用作标识符。
可以使用宏来做到这一点:
#define delete _delete
enum class EServerAction
{
create,
read,
update,
delete
};
通常不鼓励这种做法,因为现在您不能在文件的其他部分使用 delete
。但它在某些情况下很有用,例如使用 C++ 编译器编译具有名为 C++ 关键字(如 delete
)的标识符的 C 程序时。
这也会在调试器中造成混淆,因为符号的标识符与源代码中的标识符不同。
根据 C++14 标准中的 2.12 [lex.key],某些标识符 绝不能 用作标识符:
The identifiers shown in Table 4 are reserved for use as keywords (that is, they are unconditionally treated as keywords in phase 7) except in an attribute token (7.6.1) [ Note: The export keyword is unused but is reserved for future use. — end note ]:
Table 4 — Keywords
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
此外,一些标识符不得使用:
Furthermore, the alternative representations shown in Table 5 for certain operators and punctuators (2.6) are reserved and shall not be used otherwise:
Table 5 — Alternative representations
and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq
此外,根据 2.11 标识符 [lex.name],有些 非法使用 ,但编译器不需要告诉您:
some identifiers are reserved for use by C++ implementations and standard libraries (17.6.4.3.2) and shall not be used otherwise; no diagnostic is required
— Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
— Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
编辑
简答:否!
您只能使用 override
等上下文关键字作为标识符。但你不应该。该标准仅允许向后兼容,这可能会被删除或弃用。并且让它适用于每个关键字将意味着破坏该语言的许多其他语义。
考虑另一种命名枚举条目的方法。
结束编辑
注意
我试图通过使用 C-preprocessor 来实现它。
以下代码示例既不是良好做法,也不是标准所允许的。以下代码示例应仅演示 C-preprocessor 的行为。使用这些技术可能会导致问题,尤其是在使用库 headers 时,因为重新定义这些关键字会改变任何代码的语义。
尾注
对于某些名称,可以使用宏来执行此操作,这里所有关键字都可用作普通标识符,而不会丢失关键字本身的含义。首先,将它们存储到常量中或使用 nokw_true
或 nokw_bool
等有效标识符对类型进行类型定义。然后用 keyword-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
等其他基本类型,当使用 [=22= 重新声明时,将无法再使用它们,例如 unsigned long int
或 signed char
]/typedef
。类型 whar_t
、char16_t
和 char32_t
大多数时候由 typedef 声明,因此不是 "real" 关键字,但如果您的编译器直接将其实现为关键字,您可以使用 using nokw_* = *;
方法将其用作标识符。
注意 千万不要这样做! 尾注
*_cast
-关键字也可以作为 non-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
无法定义其他关键字,因为它们既不是类型,也不是像 cast-operators 那样的 function-like 关键字。在您的情况下,delete
-关键字不能与标识符同时存在,因为 delete 具有特殊的句法含义。考虑 delete ptr
或删除函数。
但是使用上面的定义可以实现带有关键字 true
和 false
的 tribool
作为枚举。 class
-关键字在这里很重要,因为名称可能与先前定义的名称冲突names/the 标识符的使用不明确。
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-entries可以像tribool::true
一样使用。
然而:许多程序员使用 CAPITALIZED enum-entry-names。因为没有大写关键字,所以这些将始终有效...
注意
这既不违反 C++ 标准也不违反 C-standard。这也是为什么经常建议使用大写宏名的原因。宏对定义后包含的所有文件全局可见,其他实体可以并且应该嵌套在命名空间中。否则可能会导致名称冲突。当您需要将某些东西定义为宏时,您应该在宏名称前添加一些独特的文本,例如您的 library/app 名称,就像您命名 C++ 名称空间一样。这也被称为命名空间,在 C++ 甚至有命名空间之前。以 OpenGL 为例,所有标识符都以 GL
为前缀,这与 C++ 名称空间一样有效,但它不允许构造为 using ns::the_class
或 using namespace
,但是你永远不应该使用它们(全球)。
尾注
我会选择
enum class EServerAction {
CREATE,
READ,
UPDATE,
DELETE
};
注意
当我写这篇文章时,我试图保留关键字的所有语义;但即使通过将关键字重新定义为宏来触及关键字也是非常糟糕的,因为标准禁止这样做。您可能永远不知道 libraries/standard 库 implementations/specific 系统 headers 如何使用或可能重新定义它们。
尾注
使用 MSVC __identifier: https://docs.microsoft.com/en-us/cpp/extensions/identifier-cpp-cli?view=vs-2019
enum class EServerAction
{
create,
read,
update,
__identifier(delete)
};