C++ contains quite a few built in data types. But these types aren’t always sufficient for the kinds of things we want to do. So C++ contains capabilities that allow programmers to create their own data types. These data types are called user-defined data types.
Perhaps the simplest user-defined data type is the enumerated type. An enumerated type (also called an enumeration or enum) is a data type where every possible value is defined as a symbolic constant (called an enumerator). Enumerations are defined via the enum keyword. Let’s look at an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Define a new enumeration named Color enum Color { // Here are the enumerators // These define all the possible values this type can hold // Each enumerator is separated by a comma, not a semicolon color_black, color_red, color_blue, color_green, color_white, color_cyan, color_yellow, color_magenta, // there can be a comma after the last enumerator, but there doesn't have to be a comma }; // however the enum itself must end with a semicolon // Define a few variables of enumerated type Color Color paint = color_white; Color house(color_blue); Color apple { color_red }; |
Defining an enumeration (or any user-defined data type) does not allocate any memory. When a variable of the enumerated type is defined (such as variable paint in the example above), memory is allocated for that variable at that time.
Note that each enumerator is separated by a comma, and the entire enumeration is ended with a semicolon.
Naming enumerations and enumerators
Providing a name for an enumeration is optional, but common. Enums without a name are sometimes called anonymous enums. Enumeration names are often named starting with a capital letter.
Enumerators must be given names, and typically use the same name style as constant variables. Sometimes enumerators are named in ALL_CAPS, but doing so is discouraged, because it risks collisions with preprocessor macro names.
Enumerator scope
Because enumerators are placed into the same namespace as the enumeration, an enumerator name can’t be used in multiple enumerations within the same namespace:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum Color { red, blue, // blue is put into the global namespace green }; enum Feeling { happy, tired, blue // error, blue was already used in enum Color in the global namespace }; |
Consequently, it’s common to prefix enumerators with a standard prefix like animal_ or color_, both to prevent naming conflicts and for code documentation purposes.
Enumerator values
Each enumerator is automatically assigned an integer value based on its position in the enumeration list. By default, the first enumerator is assigned the integer value 0, and each subsequent enumerator has a value one greater than the previous enumerator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
enum Color { color_black, // assigned 0 color_red, // assigned 1 color_blue, // assigned 2 color_green, // assigned 3 color_white, // assigned 4 color_cyan, // assigned 5 color_yellow, // assigned 6 color_magenta // assigned 7 }; Color paint{ color_white }; std::cout << paint; |
The cout statement above prints the value 4.
It is possible to explicitly define the value of enumerator. These integer values can be positive or negative and can share the same value as other enumerators. Any non-defined enumerators are given a value one greater than the previous enumerator.
1 2 3 4 5 6 7 8 9 10 |
// define a new enum named Animal enum Animal { animal_cat = -3, animal_dog, // assigned -2 animal_pig, // assigned -1 animal_horse = 5, animal_giraffe = 5, // shares same value as animal_horse animal_chicken // assigned 6 }; |
Note in this case, animal_horse and animal_giraffe have been given the same value. When this happens, the enumerations become non-distinct -- essentially, animal_horse and animal_giraffe are interchangeable. Although C++ allows it, assigning the same value to two enumerators in the same enumeration should generally be avoided.
Best practice
Don’t assign specific values to your enumerators.
Rule
Don’t assign the same value to two enumerators in the same enumeration unless there’s a very good reason.Enum type evaluation and input/output
Because enumerated values evaluate to integers, they can be assigned to integer variables. This means they can also be output (as integers), since std::cout knows how to output integers.
1 2 |
int mypet{ animal_pig }; std::cout << animal_horse; // evaluates to integer before being passed to std::cout |
This produces the result:
5
The compiler will not implicitly convert an integer to an enumerated value. The following will produce a compiler error:
1 |
Animal animal{ 5 }; // will cause compiler error |
However, you can force it to do so via a static_cast:
1 |
auto color{ static_cast<Color>(5) }; // ugly |
The compiler also will not let you input an enum using std::cin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
enum Color { color_black, // assigned 0 color_red, // assigned 1 color_blue, // assigned 2 color_green, // assigned 3 color_white, // assigned 4 color_cyan, // assigned 5 color_yellow, // assigned 6 color_magenta // assigned 7 }; Color color{}; std::cin >> color; // will cause compiler error |
One workaround is to read in an integer, and use a static_cast to force the compiler to put an integer value into an enumerated type:
1 2 3 4 |
int inputColor{}; std::cin >> inputColor; auto color{ static_cast<Color>(inputColor) }; |
Each enumerated type is considered a distinct type. Consequently, trying to assign enumerators from one enum type to another enum type will cause a compile error:
1 |
Animal animal{ color_blue }; // will cause compiler error |
1 2 3 4 5 6 7 |
// Use an 8 bit unsigned integer as the enum base. enum Color : std::uint_least8_t { color_black, color_red, // ... }; |
Since enumerators aren’t usually used for arithmetic or comparisons, it’s safe to use an unsigned integer. We also need to specify the enum base when we want to forward declare an enum.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum Color; // Error enum Color : int; // Okay // ... // Because Color was forward declared with a fixed base, we // need to specify the base again at the definition. enum Color : int { color_black, color_red, // ... }; |
As with constant variables, enumerated types show up in the debugger, making them more useful than #defined values in this regard.
Printing enumerators
As you saw above, trying to print an enumerated value using std::cout results in the integer value of the enumerator being printed. So how can you print the enumerator itself as text? One way to do so is to write a function and use an if statement:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
enum Color { color_black, // assigned 0 color_red, // assigned 1 color_blue, // assigned 2 color_green, // assigned 3 color_white, // assigned 4 color_cyan, // assigned 5 color_yellow, // assigned 6 color_magenta // assigned 7 }; void printColor(Color color) { if (color == color_black) std::cout << "Black"; else if (color == color_red) std::cout << "Red"; else if (color == color_blue) std::cout << "Blue"; else if (color == color_green) std::cout << "Green"; else if (color == color_white) std::cout << "White"; else if (color == color_cyan) std::cout << "Cyan"; else if (color == color_yellow) std::cout << "Yellow"; else if (color == color_magenta) std::cout << "Magenta"; else std::cout << "Who knows!"; } |
Once you’ve learned to use switch statements, you’ll probably want to use those instead of a bunch of if/else statements, as it’s a little more readable.
Enum allocation and forward declaration
Enum types are considered part of the integer family of types, and it’s up to the compiler to determine how much memory to allocate for an enum variable. The C++ standard says the enum size needs to be large enough to represent all of the enumerator values. Most often, it will make enum variables the same size as a standard int.
Because the compiler needs to know how much memory to allocate for an enumeration, you can only forward declare them when you also specify a fixed base. Because defining an enumeration does not allocate any memory, if an enumeration is needed in multiple files, it is fine to define the enumeration in a header, and #include that header wherever needed.
What are enumerators useful for?
Enumerated types are incredibly useful for code documentation and readability purposes when you need to represent a specific, predefined set of states.
For example, old functions sometimes return integers to the caller to represent error codes when something went wrong inside the function. Typically, small negative numbers are used to represent different possible error codes. For example:
1 2 3 4 5 6 7 8 9 10 11 |
int readFileContents() { if (!openFile()) return -1; if (!readFile()) return -2; if (!parseFile()) return -3; return 0; // success } |
However, using magic numbers like this isn’t very descriptive. An alternative method would be through use of an enumerated type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
enum ParseResult { // We don't need specific values for our enumerators. success, error_opening_file, error_reading_file, error_parsing_file }; ParseResult readFileContents() { if (!openFile()) return error_opening_file; if (!readFile()) return error_reading_file; if (!parsefile()) return error_parsing_file; return success; } |
This is much easier to read and understand than using magic number return values. Furthermore, the caller can test the function’s return value against the appropriate enumerator, which is easier to understand than testing the return result for a specific integer value.
1 2 3 4 5 6 7 8 |
if (readFileContents() == success) { // do something } else { // print error message } |
Enumerated types are best used when defining a set of related identifiers. For example, let’s say you were writing a game where the player can carry one item, but that item can be several different types. You could do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <iostream> #include <string> enum ItemType { itemtype_sword, itemtype_torch, itemtype_potion }; std::string getItemName(ItemType itemType) { if (itemType == itemtype_sword) return "Sword"; if (itemType == itemtype_torch) return "Torch"; if (itemType == itemtype_potion) return "Potion"; // Just in case we add a new item in the future and forget to update this function return "???"; } int main() { // ItemType is the enumerated type we've defined above. // itemType (lower case i) is the name of the variable we're defining (of type ItemType). // itemtype_torch is the enumerated value we're initializing variable itemType with. ItemType itemType{ itemtype_torch }; std::cout << "You are carrying a " << getItemName(itemType) << '\n'; return 0; } |
Or alternatively, if you were writing a function to sort a bunch of values:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum SortType { sorttype_forward, sorttype_backwards }; void sortData(SortType type) { if (type == sorttype_forward) // sort data in forward order else if (type == sorttype_backwards) // sort data in backwards order } |
Many languages use Enumerations to define booleans. A boolean is essentially just an enumeration with 2 enumerators: false and true! However, in C++, true and false are defined as keywords instead of enumerators.
Quiz time
Question #1
Define an enumerated type to choose between the following monster races: orcs, goblins, trolls, ogres, and skeletons.
Question #2
Define a variable of the enumerated type you defined in question 1 and initialize it with the troll enumerator.
Question #3
True or false. Enumerators can be:
3a) given an integer value
3b) not assigned a value
3c) given a floating point value
3d) negative
3e) non-unique
3f) initialized with the value of prior enumerators (e.g. color_magenta = color_red)
![]() |
![]() |
![]() |
Hey guys, is it considered a bad practice declaring your enums in the ".cpp" file? I'm not sure, because I know it is said here that you should put them in a separate header file because you can't forward declare them, but that was mentioned only as a workaround to the fact that they can't be forward declared.
I suppose that it doesn't matter for small homework academic projects but for real projects it makes your code more modular?
Thank you so much!
If you only need the enum in the .cpp file, declare it there.
There's no reason to declare it visible to the outside if the outside doesn't need it.
Hi Nascardriver, could you help me out in this sentence?
The compiler will not implicitly convert an integer to an enumerated value. The following will produce a compiler error:
What is it that you don't understand?
so, is better to use #define or enums?
for example I want to define a state machine
or
which one shall I choose? for what reason?
Thank you
Use enum class (Next lesson). Enum and #define are prone to name collisions. #define requires manual indexing. #define can cause undesired behavior (Not in this case).
Unless you want to conditionally compile code, there's rarely a reason to use #define in modern C++.
in solution 1st, there is no terminal comma.
You aren't supposed to put a comma on the final enumerator. Before C++11 it would give you an error if you did, but after C++11 you are able to use a comment on the final enumerator if you wanted to.
TLDR; You can put a comma on the final enum if you want, but you don't have to and usually if you want maximum compatibility with older compilers you shouldn't.
Greeting,
1. What is the best namespace to use enumerated type ?
2. You wondered us to avoid global variables and #define, they're hard readable (you've said)
So is there any good reason to use enumerated in global namespace ?
3. I forgot this question lol XD
And 3rd question:
Is initializing with @static_cast<enumName>(integer) safe to use ?
1. I don't understand.
2. No. Namespaces should be enum class (Next lesson) or in a namespace.
3. No. You need to validate that @integer is an enumerator of @enumName before you assign it.
The 1st and 2nd questions are almost the same.
I mean should we write enumerated in global namespace or in functions ?
If you don't declare the enum inside something else (class, struct, function), declare it in its own namespace.
For sure, Thank you ;)
@Alireza "I forgot this question" --> very cool! keep it up!
Is this allowed?
Yes, so long as COLOR_PINK is defined somewhere prior to this definition. ANIMAL_PIG will get the same integer value as COLOR_PINK.
Is it bad practice to use enumerated types just for the purpose of grouping variables without declaring its type, such as this below:
* Line 17, 19: Initialize your variables with uniform initialization. You used copy initialization.
It'd be better to use a struct or class (Covered later). For now, I don't see a problem with your solution.
Hi I made this but getting one problem I couldn't fix.
So If I put a number that's 0,1,2 or 3 the program runs fine. When I put a number less than 0 or greather than 3 this happens.
[CONSOLE]
Enter a number: 26
What is your favourite colour?
indigo // user input
Your favourite colour is indigo
Which one describes you?
1 = Aggressive
2 = Stealthy
3 = Defensive
SELECT A NUMBER
1 //user input
What is your favourite colour? // IT REPEATS THE QUESTION AGAIN! :(
Your item is a sword.
Also It leaves it as a blank sword instead of "indigo sword." It worked for when it said "your favourite colour is indigo." Why not for this? How to fix this?
Thanks for your consideration in advance.
Line 55: @number is set to 26
Line 58 -> 42
Line 69: @number is still 26
Line 69 -> 42
Hi I fixed it but I feel like it could be improved.
* You don't need to manually call @std::string::string when returning.
does the same.
* Initialize your variables with uniform initialization
* Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max()
* @printFavColor has a bad name. It's purpose isn't printing, it's reading
* Line 46, 63: Duplicate check. Remove the check in @printFavColor and only call @printFavColor is the numbers are out of range. Or do everything in @printFavColor and return a string.
* @printFavColor: Missing return-statement
Hi guys,
Regarding the use a static_cast to force the compiler to put an integer value into an enumerated type:
Could you explain how the following code is legal?
> Could you explain how the following code is legal?
It's not. "Mexican-Beer" is not a legal name and you forgot a quotation mark.
Enum classes can be constructed from integers.
Hello,
If we assign values inside enum are they evaluated during the compile time or by demand?
For example, if instead of this:
We will do this:
Will the values that enum contains going to be evaluated when each variable of type Color will be defined (which could be at any time, including runtime) and thus it would be better to use literal 0x1 instead of expression 1 << 0 in terms of performance to avoid extra evaluations or the compiler pre-evaluates expressions within enum and accordingly it doesn't have any effect on performance?
Thank you!
Hi Liam!
Compile time
Where can i improve my code
Hi Aditi!
* Line 13, 16, 19, 22, 25, 27: You don't need to explicitly call @std::string::string. You can just return the string literal and @std::string::string will be called implicitly.
* Line 30: Initialize your variables with uniform initialization.
help me only one error
Hi ayush!
Line 31: You need to pass a value to @print, eg. monstertype::monstertype_orgres.
Hello ayush.
I updated three things.
1. I've setup a input value to be passed to the print function.
2. changed print function to a void type.
3. removed assigned value to enum types
Hey Alex you talked alot in the previous lessions on how to organize the code with separate files for function declarations and constants etc. But how would we do it with enums and structs? Cause I assume you want to organize them better if they start to add up. It would be great if you could cover this or refer to a later lesson.
Basically, enums and structs are no different than functions in terms of organization. You want to put them in the smallest scope in which they are used. Typically, if they are used in a single .cpp file, they should go in that file. If they are used more broadly, then typically in a header file where they can be included into multiple files (with the headers themselves organizing related functionality).
Let's say I define the following enum:
and then want to assign the value of THING_BOX to a variable of type Thing. Should I be explicit when refering to an enum like this:
or should I just do it like this:
? I come from C# where using the type as a prefix so to speak is necessary so I'm a little confused about this.
Hi Clownfish!
Be explicit. The next lesson covers enum classes, which require you to be explicit and should be preferred over regular enums.
Hi Alex
I tried recreating your code under 'Printing enumerators' but with a switch statement instead. However it failed to compile and the compiler complained that the switch statement could not support enumerations. Did I do something wrong?
EDIT: I solved it, turns out I was just misinterpreting what the compiler was telling me, I think the problem was I put single quotation marks around the enumerator.
Hi Hervé!
Since you didn't post any code and someone else might stumble across a similar problem in the future, here's how do write @getItemName using a switch-statement:
Where can I go to get more examples and ideas of how I can practice what I'm learning here? I understand everything so far, but my fear is that without actually using the things I'm learning in any kind of useful program, I'll end up forgetting certain important things.
I'm still new to c++ so idk if I alone will be able to come up with useful real life scenarios of when I would want to use everything I'm learning.
Try typing "C++ quiz" into a search engine and see if that's close enough to what you're looking for...
But the best thing really is to give yourself challenges. One way to do that is extend the quiz programs in more complicated ways.
That's not exactly what I'm looking for, but still, that brings up good resources.
I was looking more for real-world problems developers face that I would be able to solve with code. But I guess that's not something I can really do until I get further along in the tutorial.
I like your suggestion of extending the quiz programs though. I've been doing that and it definitely helps.
I try to learn about enum types here but I just don't understand the use of enum explicitly.
Hi Samira!
Let's look at some examples
This is problematic.
* There are magic numbers.
* If we wanted to change the value of an item we'd need to change it everywhere where it's used.
* There's no way of knowing valid values for an item.
To fix these problems by using constants for the item values
This is better, but
* It involves a lot of typing or copying to declare the constants.
* We might accidentally use the same value for two different items.
* There could be value gaps between two items if we were to delete an item type.
That's were enums come in
The enumerators are automatically numbers, so we don't need to worry about duplicates or gaps.
We still need to type a lot if we want fully qualified names. Enum classes (Lesson 4.5a) will allow us to use shorter enumerator names.
Thank you for reply, nascardriver!
My conclusion are those using enum give me clearer context (by not using magic number), my program become more modular (do minimalistic change), and the item of enum guarantee be uniqe.
> item of enum guarantee be uniqe
Only if you don't manually assign the same value twice
Hi nas!
enum section
@line 13 "if (itemType == ItemType::ITEMTYPE_SWORD)"
can it be "if (itemType == ITEMTYPE_SWORD)" without the scope(::) operator as in Sir Alex's example?
It can, but it's bad. The next lesson discusses this syntax and how to avoid it.
I tried recreating this my own way:
could you please point out my mistakes?
i know it has sth to do with the main calls.
Hi Ali!
You compiler showed you a lot of the issues, you should read them.
You declared three variables, yet you never used them.
You declared @getItemName to take one int as parameter. Now you're trying to call it without passing an argument.
We had this before, that's not how to call a function. Lesson 1.4a
You declared a variable but never use it.
The parameter to @getItemName has the same name as a function, this should be avoided.
You don't need to explicitly call the std::string constructor, your compiler is smart enough to do it for you.
Inconsistent use of initializers.
Copy-pasting won't get you anywhere, you should try to understand and write this code on you own. If you feel unconfident you should go back to previous quizzes and try to solve them without copying.
Hi!
i've done it this way, is it good now?
OH and isnt this easier?
@itemType and everything within is still unused.
You're still using two different kinds of initializers.
> isnt this easier?
No, using an enum is easier, it offers named numbers that are automatically assigned values to prevent duplicates. You solution doesn't.
To keep you going, try this:
- The user enters a number (0, 1 or 2)
- Your program prints the corresponding item type
Do this by only modifying your main function. I'm asking you to do this, because you'll need to understand function parameters and arguments for all lessons.
So shall I read lesson 1.4a again? And try to understand it?
yes
Okay got it. Tomorrow is a new day!
Hi nascardriver,
will i ever have to use this in my life?
because i dont really understand this
Hi Alex,
I dont understand this part:
// ItemType is the enumerated type we've defined above.
// itemType (lower case i) is the name of the variable we're defining (of type ItemType).
// ITEMTYPE_TORCH is the enumerated value we're initializing variable itemType with.
ItemType itemType(ITEMTYPE_TORCH);
itemtype is the name of the variable, can you further explain this? and why we right it this way ItemType itemType(ITEMTYPE_TORCH); ?
Hi Eri!
> itemtype is the name of the variable, can you further explain this?
itemType is a variable like every other, it could have any other name too.
> why we right it this way ItemType itemType(ITEMTYPE_TORCH); ?
I don't know why Alex used a direct initialization here, he seems to use copy initializations almost everywhere else.
These would also work
Lesson 2.1 covers the different types of initializations available in C++.
Thanks Nascardriver. It's making more sense to me now.
I was just being inconsistent. I've updated it to use copy-initialization for now.
What does color mean and do? Not Color.
Hi Ned!
color is a variable of type Color.
You can do things like
so Color is like int, and color is variable?
Exactly, just that it doesn't store numbers but COLOR_BLACK, COLOR_RED and so on
Thanks!
it takes a while to understand all of this
Just when it finish compiling, it appears an error message that says: "enum.exe finished working"
Hi Weckersduffer!
I guess you mean the Windows error message "enum.exe has stopped working".
Your function @get doesn't return a value, this causes undefined behavior and is most likely responsible for your crash.
@main also doesn't return a value, I suggest you enabling all warnings in your compiler/IDE.
Two things are wrong:
1. Replace each "std::cout <<" in get() with "return".
2. Add return 0; to the end of main().
I think your program will work then.
Hi Alex. A small (nitpicking) doubt in the quiz. The first question asks us to "define" an enum. Shouldn't it be "Declare" an enum? And the second question should be "DEFINE a variable of the enumerated type you DECLARED in question 1 and assign it the troll enumerator." right? I'm just trying to apply what I've learnt so far like a practice!
Quiz question 1 was actually written correctly. However, the lesson incorrectly used the word "declare" instead of "define" when talking about defining an enum, which is why you are confused. I've updated the lesson as appropriate.
Quiz question 2 was wrong as you identified. I've updated the quiz question to ask you to define a variable of the enumerated type.
Thanks for pointing these out!
Hi Alex!
I'm trying to develop a simple chess engine right now , But I confused about the necessity of using enumeration data types to represent pieces of chess can't we replace it with string piece [] = "WHITE_PAWN", ... it will suffice the symbolic goal too ..
Hi Hamed!
Strings use more memory, are slower, and you could accidentally modify them at runtime. Also, when you want to check if a piece is eg. a bishop you might misspell a string causing your program to fail.
Enums are the way to go.
In addition to what nascardriver said, enums are just integers, so comparison is fast. String comparison is comparably slow.
If you later decided to change the name of an enumeration, the compiler will complain if it finds an enumerator it doesn't recognize. If you use strings, you'll have to manually update all of the strings yourself and be careful not to make any mistakes.
Enumerators can also be used as indices for arrays, whereas strings can not.
In short, use enumerators for your pieces, and then you can use these to index an array of names if you want to convert an enumerator to a string.
For Printing Strings Better way could be..
enum Color
{
COLOR_BLACK, // assigned 0
COLOR_RED, // assigned 1
COLOR_BLUE, // assigned 2
COLOR_GREEN, // assigned 3
COLOR_WHITE, // assigned 4
COLOR_CYAN, // assigned 5
COLOR_YELLOW, // assigned 6
COLOR_MAGENTA, // assigned 7
COLOR_MAX ///assiged 8
};
strings sColor[COLOR_MAX]
{
"COLOR_BLACK", // assigned 0
"COLOR_RED", // assigned 1
"COLOR_BLUE", // assigned 2
"COLOR_GREEN", // assigned 3
"COLOR_WHITE", // assigned 4
"COLOR_CYAN", // assigned 5
"COLOR_YELLOW", // assigned 6
"COLOR_MAGENTA" // assigned 7
};
void printColor(Color color)
{
if(color < COLOR_MAX)
std::cout << sColor[color];
else
std::cout << "Who knows!";
}
Yes, this is covered in the section on arrays (lesson 6.2)
In line 5 and 12, why didn't the enumerators end with a semi-colon?
Hi Hema!
The last entry of an enum doesn't need a comma, it's optional.
Using a comma after the last element makes it easier to add more entries, there's no semantic difference though.
Hello Alex and Happy New Year!
According to the C++ Core Guidelines, it seems the use of ALL_CAPS (á la macros} in enums and enum classes is frowned upon. From my own perspective, I find it more useful, handy and readable. What's your take?
Personally, I like using all caps for enumerators (and macros), but that's likely because it's what I'm used to. Google suggests that naming enumerators like constants is better since they are essentially constants, and this helps avoid naming collisions with macros.
I think they have a valid point, but I probably won't change. :)
Hi Alex,
thanks for the great tutorials!
I am playing around with sorting algorithms inspired by the later lesson on selection sort. I am aiming at writing a header file including a general "sortArray"-function, to which the user can pass an argument for picking the algorithm he wishes to apply. For this argument i defined an enum class called "Algorithm". Now the forward declaration of my "sortArray"-function inside the header file needs that enum type as well as the header's cpp-file hosting the actual function.
Is it good practice to make a header file just for the enum definition and include it to the "main-header" as well as its underlying cpp? It feels a bit wrong to me to include a header into a header. Is there any better way?
It's fine to include a header in a header if the including header needs the definitions in the included header.
However, in this case, if your Algorithm enum is only used in the header containing the function forward definitions that use the Algorithm type, then I'd probably just define Algorithm in that same header (you can always split it out later if needed).
Thanks for your answer and Merry Christmas first of all (even though it is basically over here by now).
I did not idle myself and kept digging a bit more about the topic. I read you actually stated that it is a common thing to include headers into headers (I guess in the lesson about header guards). Sorry I did not cross check enough. However, another issue in that context puzzled me a bit. I found the answer to this one as well, but I like to hear from a programming veteran, whether my interpretation on why c++ was constructed like that is correct:
The way I designed my program, the enum definition was included into my main.cpp (via the header in the header) and also into my second cpp (via the enum definition header directly). I did not pay attention first, but later remembered that having a function defined in multiple files would cause a linker error. So why not with enum definitions? Reading the wiki article about the one-definition-rule I saw that type definitions are only needed to be unique within a single translation unit. Just one possibility came to my mind, which would justify this different treatment: in some cases the compiler might need to know details about types. For example if a cpp file uses certain enumerations, it is clearly not enough to just declare the corresponding enum without defining it. On the contrary the compiler does not care about what a function is doing as long as the functions's parameters and return types fit - which is guaranteed by the forward declaration. Bottom line: in contrast to functions, multiple definitions can't be entirely avoided for types. Therefore, the one-definition-rule is less strict on types. Is that about right?
Yes, that's right. With variables and functions, the compiler only needs to know about the variable or function prototype to do syntax checking. However, with types, the full type definition needs to be known. This necessitates including the full type definition into the places where it is used. Therefore, the one definition rule is relaxed for types.
Something about the Quiz 3f
I have tested it in MSVC 2017, however, it is not a legal assignment.
And the complier gave an error that "lvalue should be a modifiable value".
So I think 3f is false?
Do I make any mistake or misunderstand about this?
My code is here:
The wording on the question was poor (and has been updated). When the enumeration is being defined, you can initialize the enumerator with any valid integer value (or value that can be converted to an integer, which includes other enumerators). However, once defined, those enumerator values can't be remapped, which is why your statement doesn't work.
Oh, I see.
Thank you.
Hi Alex,
I was about to say the same thing as user fhmadcode, above. Maybe for question 3f) you could change the word 'given' to 'initialized to'?
I've sharpened the wording in the question and the answer a bit more. Let me know if it's still confusing.
Hi Alex, I don't know why "enumerated type declaration doesn’t take any memory", can you explain something about it ?
Sure. When you're defining a user-defined type, you're just giving your type a name, and telling the compiler what it looks like. If you never use the type, no memory is needed. It's only when you declare a variable of that type that memory is allocated.
As an analogy, if I say, "An Apple is a fruit and can come in red, green, and yellow", that tells you a bit about an apple (I've defined what an apple is). But how many apples do I have? None at this point, Apple is just a definition. I don't need to reserve space in my fruit bowl until I acquire some actual apples.
I see, Thank you!
So I guess this means that the enumeration definition simply takes space in the source files, but not in the exe?
Speaking of exe files, I'm not getting any when I build my code in VS 2017. Is that simply because it's in debug mode or is there something else?
> So I guess this means that the enumeration definition simply takes space in the source files, but not in the exe?
Correct.
> I'm not getting any when I build my code in VS 2017
You should get a message in the build log (bottom of VS) telling you where the executable is located.
Oh gotcha. I never really look at the messages after it compiles successfully haha. Thanks.