19

I have been unable to find if there is a way to use a keyword in a enum definition, like:

enum class EServerAction
{
    create,
    read,
    update,
    delete
};

In C# I can use the @ char to make the compiler look at it as an identifier. Is there a way to do this in C++ (Visual Studio 2015)?

Super Rey
  • 375
  • 2
  • 11
  • 2
    You could upper-case the first letter of each enum value, if your coding standards allow for it. – Sean Apr 11 '16 at 11:33
  • 2
    rename delete to remove – MagunRa Apr 11 '16 at 13:15
  • 1
    If adding a character is allowed like `@` you said then in MSVC you can use `_` or `$`. `$delete` is a valid identifier. Otherwise just use uppercase – phuclv Apr 11 '16 at 14:14
  • @MagunRa: Principally yes, except where there's a difference between Deletion (= Destruction) and Removal (= Moving Out Of It). – Sebastian Mach Apr 11 '16 at 15:04
  • Some implementations allow using Unicode characters. For example, my VS 2013 allows `enum { á,b,c, délete};` – Peter - Reinstate Monica Apr 11 '16 at 15:40
  • Whether you want to use non-7-bit-ASCII source characters depends on the desired degree of portability. I just tried to get this enum compiled with g++, but in vain (tried different combinations of LCxx environment settings plus `-finput-charset=UTF-8`). – Peter - Reinstate Monica Apr 11 '16 at 15:51
  • @phresnel for programming purposes you are right, from the naming it seems to be a database action where it might be equal (enough). But if remove isn't a good synonym you can use destroy or something else. My point is instead of trying to trick the compiler, he should think of a different naming, identifiers are protected for a reason. – MagunRa Apr 12 '16 at 06:36
  • @MagunRa: Of course :) – Sebastian Mach Apr 12 '16 at 09:04

6 Answers6

16

No they cant be used.

From MSDN

Keywords are predefined reserved identifiers that have special meanings. They cannot be used as identifiers in your program.

The rule for identifier says:

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.
Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
  • 1
    If you want to quote an authoritative reference (but not the standard), [cppreference](http://en.cppreference.com/w/) is IMHO preferable over MSDN and learncpp. – edmz Apr 11 '16 at 11:56
  • @black:- Added the cpp reference. Although the answer still remains the same :) Thanks for pointing it. – Rahul Tripathi Apr 11 '16 at 12:05
  • Thank you! As I suspected but it would be very convenient to have something like C#. – Super Rey Apr 11 '16 at 13:54
  • @SuperRey:- I think many of the developers won't be in the favor of it as using keywords as identifier is not a recommended approach as it seems to confusing to many. Although it's my opinion but it can be a feature request. :) – Rahul Tripathi Apr 11 '16 at 13:56
  • @RahulTripathi I'm sure there are some caveats, but, in my opinion, as you have to refer to this enum as EServerAction::delete it is clear that it is not the keyword delete and even more makes the definition of the enum itself a lot more polished (in the case of http actions). Again, just my opinion :) – Super Rey Apr 11 '16 at 16:51
3

As per 2.12 [lex.key] in the C++14 standard, certain identifiers must never be used as identifiers:

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, some identifiers shall not be used:

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

Even furthermore, as per 2.11 Identifier [lex.name], some are illegal to use, but the compiler is not required to tell you:

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.

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
2

In C++ keywords cannot be used as identifiers as they can in C#.

Andreas
  • 5,393
  • 9
  • 44
  • 53
Guru HG
  • 112
  • 1
  • 2
  • @black Yeah, but only if you preceed it with an `@`, e.g. `int @class = 1;`. The reasoning behind it is that some other .NET language might not have the same set of reserved words, which allows a library on that language to define a method or variable with a name that would be inaccessible from C#, making the library unusable. (Which is against one of the .NET principles, where all languages running on .NET must be intercompatible) – This company is turning evil. Apr 11 '16 at 13:09
  • 1
    @Kroltan - Also, unlike C++, C# enum identifiers must be fully qualified. So if I had `enum MyEnum { thing, @class }`, any reference to that would have to be `MyEnum.thing` or `MyEnum.@class`. While in C++, if I had an `enum MyEnum { thing, clazz }`, I could just use `thing` or `clazz` by themselves. – Darrel Hoffman Apr 11 '16 at 14:50
2

Use MSVC __identifier: https://learn.microsoft.com/en-us/cpp/extensions/identifier-cpp-cli?view=vs-2019

enum class EServerAction
{
    create,
    read,
    update,
    __identifier(delete)
};
NN_
  • 1,593
  • 1
  • 10
  • 26
0

It is possible to do this using macros:

#define delete _delete

enum class EServerAction
{
    create,
    read,
    update,
    delete
};

This practice is usually discouraged because now you cannot use delete in other parts of the file. But it is useful in some situations like when compiling a C program that has identifiers named as C++ keywords (like delete) using a C++ compiler.

This will also cause confusion in the debugger because the identifiers for the symbols are not the same identifiers that are in the source code.

wefwefa3
  • 3,872
  • 2
  • 29
  • 51
-1

EDIT
Short answer: NO!

You can only use contextual keywords like override as identifiers. But you shouldn't. The standard only allows this for backwards compatibility, which might be removed or deprecated. And also making it to work for every keyword would mean to break many other semantics of the language.

Think about an alternative way to name the enum entries.
END EDIT

NOTE
I tried to make it possible by using the C-preprocessor. The following code examples are neither good practice nor allowed by the standard. The following code examples should only demonstrate the behavior of the C-preprocessor. Using these techniques could cause problems especially when using library headers, because redefining these keywords can change the semantics of any code.
END NOTE

With some names, it is possible to do this with macros, here are all keywords made useable as normal identifiers without loss of meaning of the keywords itself. At first, storing them into a constant or typedefing the types with valid identifiers like nokw_true or nokw_bool. Then defining macros with the keyword-name to refer to the aliases.

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

NOTE
I tested this code and it worked as if the macros were never defined. But there is no guarantee that it behaves the same on every C++ compiler, because it is not the same, keywords are very different from identifiers. And it is not forward compatible with newer C++ standarts, because they could add new syntax using these keywords, which would be invalid with identifiers under the hood.
END NOTE

With other fundamental types like int or char it wouldn't be possible anymore to use them like unsigned long int or signed char when redeclared with using/typedef. The types whar_t, char16_t and char32_t are most of the time declared by a typedef and so not a "real" keyword, but if your compiler implements it directly as a keyword, you can make it useable as an identifier with the using nokw_* = *; approach.
NOTE Just don't do it! END NOTE

The *_cast-keywords may also be made as a non-keyword, by defining them in a function:

    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

Other keywords cannot be defined, because they are neither a type nor a function-like keyword as the cast-operators. In your case the delete-keyword cannot exist with the identifier at the same time, because delete has special syntactic meaning. Consider delete ptr or deleted functions.

But using the definitions above it is possible to implement a tribool with the keywords true and false as an enum. The class-keyword is important here, because the names may collide with the previously defined names/the use of the identifiers is ambigious.

enum class tribool { true, maybe, false };

Or it could alse be in a namespace:

// 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
};

The enum-entries can be used like tribool::true.

HOWEVER: Many programmers use CAPITALIZED enum-entry-names. Because there are no capitalized keywords, these will always work...
NOTE
This is no violation against the C++-standard nor the C-standard. This is also why it is often recommended to use CAPITALIZED macronames. Macros are globally visible to all files included after the definition, other entities can and should be nested in namespaces. Otherwise it might lead to name clashes. When you need to define something as a macro, you should prepend some unique text to the macroname like your library/app name as you would name a C++-namespace. This was also called namespaces, before C++ even had namespaces. Look at OpenGL for example, all identifiers are prepended with GL, this works as good as C++ namespaces, but it does not allow constructs as using ns::the_class or using namespace, which however you should never use (globally).
END NOTE

I would go with

enum class EServerAction {
    CREATE,
    READ,
    UPDATE,
    DELETE
};

NOTE
When I wrote this, I tried to keep all the semantics of the keywords; but even touching the keywords by redefining them as macros is very bad, because it is forbidden by the standard. You might never know how libraries/standard library implementations/specific system headers use or potentially redefine them.
END NOTE

cmdLP
  • 1,658
  • 9
  • 19
  • The C++ standard disallows #defining macros which are keywords. – Sneftel May 20 '19 at 12:25
  • @Sneftel Except there is no violation of this rule when using the C-preprocessor as a separate program. Some compilers implement the C-preprocessor as a seperate program, which just transforms text (in this case source code). The result might be a valid program but it might also not. There might be problems, when libraries use the now redefined kewords, there is also the common problem/risk for a library user to define anything (even header guards), which might lead to some bugs. – cmdLP May 20 '19 at 20:54
  • 2nd: The solution to this problem is either not to use macros (for beginners), to add "namespaces" to the identifiers (by prepending some unique text (like your library/app name) to each identifier) or by only using the macros in a well defined scope (1st define the macro, 2nd use it and then 3rd undefine it. Between these, you never include any (foreign) header). This is always well defined. The standard itself is often very strict about things, because it is easy to break things. – cmdLP May 20 '19 at 21:03
  • 3rd: But when looking at the C-preprocessor as a text transformation tool without any connection to a language (C/C++), there shouldn't be any problems to use it as a text transformation tool. You might ask to the define-use-undefine method (when using short macro names) "what if another library header defined such macro already?" -- "Then it is probably a badly written library or you didn't read the documentation". It is as bad to redefine keywords as macros as it is to redefine function names as macros. Some (C) libraries implement some functions as macros without even documenting it. – cmdLP May 20 '19 at 21:13
  • 4th: For standard library implementors, it is permitted to implement standard functions as macros. Redefining these functions as macros (eg. for debugging) could result in bugs/errors too. Consider a C standard library implemented malloc as `#define malloc(sz) realloc(NULL, (sz))`. This is as far as I know fully standard conform (when realloc itself is). I often saw how malloc is wrapped in a macro to debug allocation etc.. This would not work in this case. – cmdLP May 20 '19 at 21:23
  • 5th: This post was written by me more than a year ago. It shows indeed a very bad technique of redefining keywords as macros. I did know that, but it should only show how macros could be used in this context. But the point is, such code would never be written in user code, but standard library implementors often use these tricks to workaround limitations/bugs of some compilers. A std library implementor knows the compiler (for which the code is written) very well. Using compiler builtins could also be forbidden in the same way, but when knowing the used compiler and version. It is well defined – cmdLP May 20 '19 at 21:33
  • Last: By using the preprocessor itself with the help of conditional `#if`-statements the *bad* code is always restricted to a well known compiler environment. When some foreign compiler is used, there could be a bunch of unknown things happening, without conditional compilation. This *bad* code is often used for optimization or error detection/UB sanitizer, which is unspecified by the standard. – cmdLP May 20 '19 at 21:39