2

In C++11, we have scoped enum, and we can using it as follows.

#include <iostream>
enum class Color
{
    RED,
    BLUE,
};
int main()
{
    Color color = Color::RED;
    if (color == Color::RED)
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

I have already using the scoped enum everywhere in my project.

Now I must move to C++98, so scoped enum can not be used anymore.

How can I implement a scoped enum in C++98 and using just like the one in C++11?

If the implement technique is compilicate, can we extract it into template?

Follow link have already talk about some technique, but not as simple as C++11.

How to use enums in C++

For example:

namespace Color
{
enum MyColor
{
    RED,
    BLUE,
};
}

Thanks for your time.

rustyx
  • 80,671
  • 25
  • 200
  • 267
Xu Hui
  • 1,213
  • 1
  • 11
  • 24
  • Does this answer your question? [Do we really need "enum class" in C++11?](https://stackoverflow.com/questions/6936030/do-we-really-need-enum-class-in-c11) – TonySalimi Apr 17 '20 at 12:15

2 Answers2

2

A common way to emulate scoped enums prior to C++11 is to declare the non-scoped enum within a class declaration:

#include <iostream>

struct Color {
   enum MyColor {
      kRed,
      kBlue
   };
};

int main() {
    const Color::MyColor color = Color::kRed;
    if (color == Color::kRed)
    {
        std::cout << "red" << std::endl;
    }
}

or, as a minor variation, for some brevity at the possible cost of some confusion (i.e., at the use site: "how does MyColor and Color relate?"):

#include <iostream>

struct Color {
   enum MyColorImpl {
      kRed,
      kBlue
   };
};

typedef Color::MyColorImpl MyColor;

int main() {
    const MyColor color = Color::kRed;
    if (color == Color::kRed)
    {
        std::cout << "red" << std::endl;
    }
}

Of course, you can also use the namespace scoping approach shown in your question, but it comes with the potential problem the the namespace may be expanded elsewhere in your code base, which in turn could lead to unexpected (developer expectations) behaviour; your emulated scoped-enum could e.g. start to behave as a concatenation of several different non-scoped enums.

#include <iostream>

namespace color {

enum MyColor {
   kRed,
   kBlue
};

}

namespace color {

enum CarColor {
   kAbsolutelyNotRed
};

}

int main() {
    const color::MyColor color = color::kRed;
    // At best, a -Wenum-compare warning.
    if (color == color::kAbsolutelyNotRed)
    {
        // At worst, a critical logical fault.
        std::cout << "absolutely not red (ups, actually red)" << std::endl;
    }
}
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • It works, but using Color::MyColor looks long, so I wonder is there any technique can get rid of it. – Xu Hui Apr 17 '20 at 10:47
  • Sir, thanks point out the weakness of namspace based enum. Does the struct based enum a standard practice in the community? – Xu Hui Apr 17 '20 at 10:52
  • 1
    @XuHui You could, possibly for some confusion, rename the non-scoped `enum` as, say `MyColorImpl`, and expose an alias to the enum type: `typedef Color::MyColorImpl MyColor;`. This would allow you to use `MyColor` when declaring an enum variable, and `Color::kRed` when accessing its enumerators. Regarding struct scoping vs namespace scoping, I would say this comes down to preference. I prefer the strong typing of scoping it within a class. – dfrib Apr 17 '20 at 10:54
  • 1
    @XuHui I expanded with an example of how the namespace approach could lead to some confusion (in case the useful warning from a good compiler is ignored). – dfrib Apr 17 '20 at 11:03
  • Sir, using class based enum compare will also leads to the `-Wenum-compare` warning. The example code shown as follow. ```#include class Color { public: enum MyColor { kRed, kBlue }; }; class CarColor { public: enum MyCarColor { kAbsolutelyNotRed }; }; int main() { const Color::MyColor color = Color::kRed; if (color == CarColor::kAbsolutelyNotRed) { std::cout << "absolutely not red (ups, actually red)" << std::endl; } }``` – Xu Hui Apr 17 '20 at 11:10
  • 1
    @XuHui yes but with a class based enum the scoping semantics is different between the two: you would be semantically prompted that comparing a `CarColor::kAColor` against a `MyColor` would not be a good idea; they have different scoping. However, for the namespace case, the both scope under `color`, meaning you do not see from the use site to what enum `kColor` belongs to in the `color::kColor` expression. This is just barebones sematnics, though, for true type safety you could implement specific classes to emulate enums (there are lots of existing biolerplate out there for this purpose). – dfrib Apr 17 '20 at 11:15
  • Sir, due to your kind explaination, I now fully understand. What do you mean of `discrete class`? You mean let each enum item be a class? like `red` class, `blue` class? Could you please share some reference? – Xu Hui Apr 17 '20 at 11:19
  • 1
    @XuHui Sorry, _discrete_ was the wrong term, I have now edited it to _specific_. Basically a class that acts somewhat like an enum, but with strong typing. This usually comes with some boilerplate, though, and I personally just resort to scoping enums within classes in pre-C++11 (combined with `-Wall, -Werror` as well as a good static analysis tool). See e.g. [this article](https://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955) for an example. – dfrib Apr 17 '20 at 11:23
  • Sir, Thanks for your sharing, I will have a look at it. And I found the `Month` example in book Item 18 is also a good practice~ :) – Xu Hui Apr 17 '20 at 11:29
  • @XuHui Yes that is a great reference! – dfrib Apr 17 '20 at 11:32
0

As drfi already give a detailed explaination, I still give here another perspective to solve the problem.

We can using class to implement the enum, and support strict type check.

The code is modified from the class Month example in book Effective C++ 3rd: Item 18

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}
Xu Hui
  • 1,213
  • 1
  • 11
  • 24