Other data types

1. What's the point of using union when we have struct??
In union we can't even know value of something because they share the same memories, but in struct we can control the value and can know the exact value of it.

2. Code :
1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

enum month
{Jan=1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Des};

int main()
{
    month k2;
    k2 = May;
    cout << k2;
}

In here, the output is 5.But, can I make the output become May??
If its possible how is it??

3. Code :
1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

enum  class month
{Jan=1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Des};

int main()
{
    month k2;
    k2 = month :: May;
    cout << k2;
}

In here, I cant even output it now, why is it??

4. enum class EyeColor : char {blue, green, brown};
Does it have any effect with adding char (a type) in here??
What is the benefit of using it??

Thanks..
Last edited on
union is dated, but it is NOT ANYTHING LIKE A STRUCT.

if you had this:
union u
{
uint32_t i;
char c[4];
};

u v;
v.i = 1234;
v.c[0] = 11;
cout << v.i << endl; //what do you get?

well, first, this isnt entirely legal in pure c++ but relaxed compiler settings allow it.
if you did it with a relaxed compiler, you would see that i is no longer 1234 but some other value. this is because the parts of a union ALL SHARE the same memory. A union, then, is a morphic type that can be any of the things inside it, but not all of them. You get to pick ONE of them. A struct has ALL of them at once, its a container of all the types. Standard c++ wants you to only use one of the parts of a union for a specific variable, it can't change type later in other words. A lot of old code exploited the fact that all compiler support (with relaxed / default settings) allowing this, so you could use them to get at the raw bytes of something easily by unioning it with a char array etc. (which you can do with a pointer cast, but it saved bloat to union).

I highly recommend against using a union. Modern c++ uses variant and std::any for these ideas. You don't normally need a lot of this kind of thing. What is it good for? Say you had to convert matlab code to c++ ;) you have an array of something. array[0] is an integer, array[1] is a string, and array[3] is a double. The array has billions of elements, so you wold rather not waste memory on a full struct that has each type. Void* is clunky. .... this is a simple example of using these things, but again, its a little weird in most c++ code to have that kind of mess to begin with.

There is no nice way to get the text version of an enum. (I think there is an evil macro way to do it).
the nice way is just parallel arrays:
num month
{Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Des, maxmonths}; //got rid of =1 start from 0 is easier to code, work around this elsewhere

string months[maxmonths] = {"Jan"... };
cout << months[Jan] << endl; ^^^
while parallel arrays work, you 'probably' want to wrap them up in a class for any serious work -- tightly coupled (logically) but independent items are nothing but trouble in large projects.

3) I have not fooled with the enum class yet, and am unsure what all it can do. I don't think it solves the above problem; someone else will have to help you here.

4) as 3, missed that it was the class again.
Last edited on
Working backwards,

In 3, the type is month, a class at this point. This created a type other than an enum type. The stream operators don't know what a "month" type is. In the previous example, 2, the type is an enum named month, which is recognized by the stream operator, and can be output.

In the case of 3, since it is a type month (not an enum), like any custom type, the stream operator must be given an overload for the << operator in order to know how to process it.

Rather than use the word "benefit", I'd prefer the word "purpose" - as benefit implies a judgement I'd rather not impose. The purpose of enums is to create a numeric...enumeration. It subsequently forces an association between those values in the enum and assignment to instantiations of that enum so that only those values listed can be assigned In the case of a month, only valid numeric values can be assigned (not zero, nothing above 12, nothing negative).

That is it's purpose. It is a compile time concept. Enums can be used as case identities in switches. They can be compared.

The notion that you could print the text of the enum label is not supported in the language, as they do not generate any string literal types and aren't intended to. There are patterns of coding using enums in various contexts (search the Internet for 'the enum trick').

In order to obtain a string relating to the enum, one must form an array, likely from string literals in this case, but consider what language to choose. In simple, student level programs it may seem obvious this particular list is applicable, but in commercial and professional work there is usually an obvious requirement for language independence, in which case the numeric nature of the enum returns as an advantage rather than a disadvantage, for now the substitution of the month into any language is more likely, given some mechanism for lookup by language.
1. unions can save memory space, something useful even with C++. Not for everyday use, though:
https://stackoverflow.com/questions/4788965/when-would-anyone-use-a-union-is-it-a-remnant-from-the-c-only-days

2. Writing out the names of the months from an enumerated value can be a bit complicated (C++11 or later required for this example because of the vector's initialization):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
#include <string>
#include <vector>

enum month { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

int main()
{
   month k2 = May;
   std::cout << k2 << '\n';

   std::vector<std::string> months { "January", "February", "March", "April",
                                     "May", "June", "July", "August",
                                     "September", "October", "November", "December" };

   std::cout << months[k2 - 1] << '\n';
}
5
May

*Note: this is just one method for doing what you want.

3. enum classes are NOT old style C++ enums.
https://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html

*You could overload std::ostream's operator<< so printing out the the name of the month is done "automatically:"

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
#include<iostream>
#include <string>
#include <vector>

enum month { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

std::ostream& operator<<(std::ostream& os, const month& v);

int main()
{
   month k2 = May;

   std::cout << k2 << '\n';
}


std::ostream& operator<<(std::ostream& os, const month& v)
{
   std::vector<std::string> months { "January", "February", "March", "April",
                                     "May", "June", "July", "August",
                                     "September", "October", "November", "December" };

   std::cout << months[v - 1];

   return os;
}
May
Last edited on
1. What's the point of using union when we have struct?

It expresses "I have exactly one of these objects" to the reader. Additionally, it allows particular kinds of memory-oriented optimizations. For example, union is used to seriously improve the performance of standard library components like std::string and std::any, by squeezing the contents of the string into the object and thereby eliminating allocations when the contents are small.

This is called small buffer optimization. Its particulars are interesting, but not very simple.

It's more important to understand the topic of discriminated unions. You'll write a struct like which contains some kind of flag telling you which variant member of the union is active. In the case of token, its member of type kind communicates this information.

(token is a union-like class because it contains an unnamed union as a member.)

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
enum class kind { int_, double_ };

struct token 
{
  explicit token(int i)
    : k_{kind::int_}
    , i_{i} 
  {}

  explicit token(double d)
    : k_{kind::double_}
    , d_{d}
  {}

  bool is_int()    const { return k_ == kind::int_; }
  bool is_double() const { return k_ == kind::double_; }
  kind get_kind()  const { return k_; }

  int as_int()       const { return i_; };
  double as_double() const { return d_; } 

private:
  union { int i_, double d_; };
  kind k_;
};


2. In here, the output is 5.But, can I make the output become May??

Tell the compiler what it means to say some_ostream << some_month by defining the following function:

1
2
3
4
5
6
7
8
9
std::ostream& operator<< (std::ostream& s, month n)
{ 
  switch (n) 
  {
    case Jan: return s << "Jan";
    case Feb: return s << "Feb"; 
    // ... etc.
  }
}

This is called operator overloading.

3. In here, I cant even output it now, why is it??

Scoped enumerations can't be implicitly converted to (or from) an integer. This makes them a safer choice in most cases; you need to explicitly convert, or follow the previous answer.
 
std::cout << static_cast<int>(k2) << '\n';


4. Does it have any effect with adding char (a type) in here??

It specifies that the underlying type of the scoped enumeration is char.

An object of enumeration type can take all the values of its underlying type (by default). Correspondingly, the size of a scoped enumeration is the size of its underlying type, e.g., sizeof (EyeColor) == sizeof (char), and an object of type EyeColor can hold any char.

There isn't necessarily any benefit to doing this. It does possibly help enable memory optimizations, and it can be used to implement integer-like types with unique behavior.

For example, std::byte is designed to represent part of another object whose representation we don't know. For example, we can treat an int as a sequence of std::byte; but we are forbidden from adding and subtracting bytes.

Its definition is
enum class byte : unsigned char {};
See
https://en.cppreference.com/w/cpp/types/byte
Last edited on
C++11 or later required for this example because of the vector's initialization
Don't be afraid to use C-strings and arrays when the problem calls for them. Also use static to prevent the data from being created/deleted on every call. This is a perfect case:
1
2
3
4
5
6
7
8
9
10
std::ostream& operator<<(std::ostream& os, const month& v)
{
   static const char *months[] { "January", "February", "March", "April",
                                     "May", "June", "July", "August",
                                     "September", "October", "November", "December" };

   std::cout << months[v - 1];

   return os;
}
Last edited on
There used to be an issue with alignment in vectorization; the language had no support for alignment. The union did, as by-product of its nature, help the alignment.
Also use static to prevent the data from being created/deleted on every call.
or if you need it elsewhere, make it a const static class member.
Last edited on
Topic archived. No new replies allowed.