Click here to Skip to main content
Click here to Skip to main content

Enum vs Const

By , 5 Oct 2003
 

Introduction

I recently had to review some c++ code written by someone else. The code was well written. It checked for error conditions and did reasonable things if something went wrong. But there was something about it that irked me.

For obvious reasons I'm not showing the real classes. Instead I'll illustrate the irksome thing with contrived examples.

The Original Class

The class I was reviewing ran something like this.
class CAClass
{
public:
    static const UINT aConstantOfInterestToThisClass1 = 0;
    static const UINT aConstantOfInterestToThisClass2 = 1;

    CAClass(UINT constValue, LPCTSTR someOtherParameter);

    void DoSomething();

private:
    UINT    m_const;
    CString m_parameter;
};

The class CAClass constructor takes a parameter that specifies something of interest to the class, constValue and a pointer to a string. DoSomething() does something relevant based on the parameters passed to the constructor. The class implementation was coded something like this.

CAClass::CAClass(UINT constValue, LPCTSTR someOtherParameter)
{
    m_const = constValue;
    m_parameter = someOtherParameter;
}
    
void CAClass::DoSomething()
{
    switch (m_const)
    {
    case aConstantOfInterestToThisClass1:
        //  Do something defined by this constant.
        break;

    case aConstantOfInterestToThisClass2:
        //  Do something defined by this constant.
        break;

    default:
        //  It's not a valid value, raise an exception
        RaiseException(ERROR);
        break;
    }
}

This code will work fine. So what's wrong with it?

How I'd have coded it

class CBClass
{
public:
    enum constsOfInterestToThisClass
    {
        bConstantOfInterestToThisClass1 = 0,
        bConstantOfInterestToThisClass2,  // The value will be the 'next' 
                                          // value
    };

    CBClass(constsOfInterestToThisClass constValue,
            LPCTSTR someOtherParameter);

    void DoSomething();

private:
    constsOfInterestToThisClass m_const;
    CString m_parameter;
};

and the implementation...

CBClass::CBClass(constsOfInterestToThisClass constValue,
                 LPCTSTR someOtherParameter)
{
    m_const = constValue;
    m_parameter = someOtherParameter;
}
    
void CBClass::DoSomething()
{
    switch (m_const)
    {
    case bConstantOfInterestToThisClass1:
        //  Do something defined by this constant.
        break;

    case bConstantOfInterestToThisClass2:
        //  Do something defined by this constant.
        break;

    default:
        //  It's not a valid value, raise an exception
        //  We should never get here...
        RaiseException(ERROR);
        break;
    }
}

There's almost no difference. Any good c++ compiler would compile identical code for both classes.

So what's the difference?

The first class, CAClass, defines a bunch of constant values of interest to itself. CBClass defines the same named constants but it does it as an enum. An enum defines a limited set of valid values and can also be used as a pseudo datatype. Look at the difference in the definitions of the constructors.
CAClass(UINT constValue, LPCTSTR someOtherParameter);

CBClass(constsOfInterestToThisClass constValue, LPCTSTR someOtherParameter);

CAClass can accept any valid UINT value. That's over 4 billion possible values, only 2 of which are of any possible interest to the class. Any valid UINT value outside of 0 or 1 will cause the DoSomething function to raise an exception that other code within your application must handle.

CBClass in contrast will accept only one of the two enum values. Try and pass any invalid constant and the compiler will (should) complain with an error or warning message.

Contrast this

CAClass obj(1000, _T("This is a string"));

obj.DoSomething();

with this

CBClass obj(1000, _T("This is a string"));

obj.DoSomething();

The first example CAClass obj(1000, _T("This is a string")); will compile and throw an exception at runtime when it calls obj.DoSomething(). The second example CBClass obj(1000, _T("This is a string)); will at the very least throw up an error message in your compiler, at compile time. A good implementation will fail to produce an executable file until you've corrected the error and provided a valid value. VC++ flags a warning but produces an executable if you've set error level to 3 and not checked 'warnings as errors'. I always compile my code at error level 4 and 'warnings as errors'.

The CBClass constructor expects a first parameter of type constsOfInterestToThisClass. This may be either bConstantOfInterestToThisClass1 or bConstantOfInterestToThisClass2 or a variable of type constsOfInterestToThisClass. The compiler will let you define a variable of type constsOfInterestToThisClass but will only let you assign values from the enum values you define.

CBClass::constsOfInterestToThisClass var;

var = CBClass::bConstantOfInterestToThisClass1;  // OK

var = 47;  // Error

Another issue

From reading the foregoing it's tempting to conclude that the final default: case in CBClass::DoSomething() is superfluous. You might even have thought I left it in by mistake. After all, if you've used an enum correctly the default: should never occur. That's true today. But what if you add a new enum constant sometime down the track and forget to add code to the DoSomething() function to handle it? If your switch statement silently ignores enum values it doesn't know about you run the risk of incurring all kinds of unexpected (and difficult to trace) behaviour. Leaving the default: case in place greatly increases your chances of catching such oversights during program development and testing.

Casts

As one or two readers have pointed out it's possible to defeat the whole point of this article by using casts.  For example one could code my error example from above thusly:

CBClass::constsOfInterestToThisClass var;

var = CBClass::bConstantOfInterestToThisClass1;  // OK

var = (CBClass::constsOfInterestToThisClass) 47; // Compiles

and the compiler will happily compile your code.  Of course it won't run as you expected but if you left the default: code in the switch statement at least you'll get an exception at runtime and hopefully during program testing.  What you're doing here of course is saying to the compiler, in effect, 'you know and I know that 47 isn't a valid constant here but I know better than you do so just go ahead and compile it for me'.  Once you've asserted your superior knowledge to the compiler all bets are off.

Interestingly, the compiler considers some casts to be so extreme that it still won't compile them without an intermediate step.  Ie, cast something to something else, then cast that something else to the final type.

Conclusion

The compiler will do a lot of error checking for you at compile time, if you let it. Using enum's rather than const's helps the compiler find places in your code where you've made incorrect assumptions. The compiler's a lot more thorough than most of us are when it comes to checking datatypes!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rob Manderson
United States United States
Member
I've been programming for 35 years - started in machine language on the National Semiconductor SC/MP chip, moved via the 8080 to the Z80 - graduated through HP Rocky Mountain Basic and HPL - then to C and C++ and now C#.
 
I used (30 or so years ago when I worked for Hewlett Packard) to repair HP Oscilloscopes and Spectrum Analysers - for a while there I was the one repairing DC to daylight SpecAns in the Asia Pacific area.
 
Afterward I was the fourth team member added to the Australia Post EPOS project at Unisys Australia. We grew to become an A$400 million project. I wrote a few device drivers for the project under Microsoft OS/2 v 1.3 - did hardware qualification and was part of the rollout team dealing directly with the customer.
 
Born and bred in Melbourne Australia, now living in Scottsdale Arizona USA, became a US Citizen on September 29th, 2006.
 
I work for a medical insurance broker, learning how to create ASP.NET websites in VB.Net and C#. It's all good.
 
Oh, I'm also a Kentucky Colonel. http://www.kycolonels.org

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralUndeclared Identifier ErrormemberLazyKancha10 Apr '06 - 18:51 
Hi,
 
I have a small problem when using enums with VC++. Although, I use enums a lot with C#, I am having problems using them in VC++. Let me explain.
 
I have the declaration in the header file:
 
class CContext
{
private:
 
public:
enum ContextType
{
Context1 = 0,
Context2
} ;
 
CContextMenu() ;
virtual ~CContextMenu() ;
bool AddContextItems( ContextType cT ) ;
}

 
Then I have the implementation in the cpp file:
 
bool AddContextItems( ContextType cT )
{
//Do something
return true ;
}

 
But on compilation, I get the error, ContextType undeclared identifier.
Also, when I click on the function AddContextItems in the Workspace explorer window, it says, Cannot find defination(Implementation) of this file. Can you tell me why this happens?
 
Thanks,

 
---
With best regards,
A Manchester United Fan
 
The Genius of a true fool is that he can mess up a foolproof plan!
GeneralRe: Undeclared Identifier ErrormemberVibhash Jha3 May '06 - 1:50 
just a guess..
in the implementation u have to provide class name (CContext::AddContextItems )too...
otherwise it should work fine..
 
cheers
vibhash

 
Vibhash Chandra Jha
Delmia Solutions, Bangalore
QuestionWhat if it must be specified at runtime ?membercedric moonen6 Apr '05 - 8:46 
Hi ! Great article.
But I have a little question: what happens if this parameter is defined at runtime ? For example it must be provided by reading the value in a configuration file ? Your solution won't work anymore here. I am wrong ?
QuestionWhat am I doing wrong?sussAnonymous23 Nov '04 - 21:56 
typedef unsigned int UINT;
typedef char* LPCTSTR;
typedef char* CString;
 
class CAClass
{
public:
static const UINT aConstantOfInterestToThisClass1 = 0;
static const UINT aConstantOfInterestToThisClass2 = 1;
 
CAClass(UINT constValue, LPCTSTR someOtherParameter);
 
void DoSomething();
 
private:
UINT m_const;
CString m_parameter;
};

 
cl test.cpp
 
test.cpp
test.cpp(9) : error C2252: 'aConstantOfInterestToThisClass1' : pure specifier can only be specified for functions
test.cpp(10) : error C2258: illegal pure syntax, must be '= 0'
test.cpp(10) : error C2252: 'aConstantOfInterestToThisClass2' : pure specifier can only be specified for functions

AnswerRe: What am I doing wrong?memberVibhash Jha3 May '06 - 1:41 
static data variables can't be initialized within class..
initilaize it outside using scope resolution operator
UINT CAClass::aConstantOfInterestToThisClass1 =0;
....
cheers
vibhash

GeneralConst still neededmemberCrawfis29 Oct '03 - 9:07 
While we are at it. Many many times consts are left out of the constructors and other member functions. Can we add consts to the Constructors for better education of when consts are a good idea.
 
Roger
 
Roger A. Crawfis
Associate Professor
Computer & Information Science
The Ohio State University
GeneralAnother good reason for noy using constsmemberxprsg21 Oct '03 - 14:17 
Most new compilers (e.g. msvc) do not optimise the consts out of the application, for obvious implications in classes etc. They keep the data around just to keep the numbers right, and whereever required substitute the value of the const instead of the referencing the const for the value, thus giving you the speed of an actual const. But you loose space in the binary only to gain the syntactical advantage during development.

GeneralToo bad enums are integer onlymemberSébastien Lorion11 Oct '03 - 20:46 
I personally would be happy if enums could be of any value type (including strings).
 
It happened to me a couple of times where a Double or a String enum would have been handy ...
 
That said, enums are great, especially when used as flags.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralRe: Too bad enums are integer onlyeditorRob Manderson12 Oct '03 - 0:36 
String enums would be great but hard to enforce without a lot of compiler support. Remember a string is really a pointer to an array of chars, so the compiler would have to be sure that the pointer being used could only ever point to an array of chars defined in the enum. Aliasing would have to be outlawed if one used a string enum.
 
On the other hand... one could fake string enums by using a class reference where the strings were private or protected members and the enum 'value' is an integer enum.
 
class stringenum
{
public:
    enum enumval
    {
         value1,
         value2,
         ...
         endval
    };
 
    char operator*(enumval val) { return m_szVal[val]; }
 
private:
    char *m_szVal[endval];
};
Which would allow code such as the following...
class CSomeClass
{
public:
    CSomeClass(stringenum::enumval val);
 
private:
    char *m_privateVal;
};
 
CSomeClass::CSomeClass(stringenum::enumval val)
{
    stringenum enumInstance;
 
    m_privateVal = strdup(enumInstance[val]);
}
where CSomeClass could only be instantiated by instantiating an instance of the stringenum class which in it's turn requires the use of a valid string enum value.
 
But it's a bit of a workaround ain't it... Smile | :)
 
I suspect floating point enums are disallowed because of their imprecision. Ie, if a double or float value were specified in the enum as a value with 2 digits to the right of the decimal point, could one allow a value that's identical but for the eighth digit to the right of the decimal point? Ie,
enum floatvalues
{
    val1 = 2.00,
    val2 = 2.55
};
 
is 2.00 close enough to 2.00000001 for the purpose of an assignment?

 
Rob Manderson
 
http://www.mindprobes.net
 
"I killed him dead cuz he was stepping on my turf, cutting me out of my bling the same way my ho cuts cookies, officer"
 
"Alright then, move along"
- Ian Darling, The Lounge, Oct 10 2003
GeneralRe: Too bad enums are integer onlymemberSébastien Lorion12 Oct '03 - 9:22 
Well, if it was possible to inherit from System.Enum, that would simplifies things a little bit Smile | :)
 
In S#, I think it's possible, but haven't tried it. I also don't know if the derived type would be CLS-compliant.
 
I understand your points, but those are "technicalities" that get in the way of consistency of the .NET framework.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralI find enums very usefulmemberDamir Valiulin10 Oct '03 - 6:44 
I like your article.
 
I also found enums very useful. In my coding practice I "recycle" a lot of code by copy/paste. A lot of time, the only difference is the constant. With enums, compiler catches a lot of bugs where I would forget to replace the constants of original code with new constants of different type.
GeneralRe: I find enums very usefuleditorRob Manderson12 Oct '03 - 0:37 
Thanks Smile | :)
 
You make a good point.
 
Rob Manderson
 
http://www.mindprobes.net
 
"I killed him dead cuz he was stepping on my turf, cutting me out of my bling the same way my ho cuts cookies, officer"
 
"Alright then, move along"
- Ian Darling, The Lounge, Oct 10 2003
QuestionTypo ?memberNick Seng8 Oct '03 - 16:30 
I'll be the first to admit that I haven't done C++ in a loooong time, but shouldn't the following code have an extra closing bracket?
 
CAClass obj(1000, _T("This is a string");
 
or have I really been away that long?Unsure | :~

 









Support Bone

AnswerRe: Typo ?editorRob Manderson8 Oct '03 - 18:59 
Nope, it's a typo Frown | :( Fixing now!
 
Rob Manderson
 
http://www.mindprobes.net
 
You have an eight-ball and Tommy wants to buy two grams. You bought the eight-ball with a quarter-bag of grass, two reds, a six-pack of Old Milwaukie, and $4 in change. You want a profit margin of 35%. How much hash should you get from Tommy for your blow? - Roger Wright
GeneralRe: Typo ?memberNick Seng8 Oct '03 - 19:45 
Happens to the best of us, mate.Smile | :)
 









Support Bone

GeneralRe: Typo ?editorRob Manderson9 Oct '03 - 0:41 
And to me too, mate Smile | :)
 
Rob Manderson
 
http://www.mindprobes.net
 
You have an eight-ball and Tommy wants to buy two grams. You bought the eight-ball with a quarter-bag of grass, two reds, a six-pack of Old Milwaukie, and $4 in change. You want a profit margin of 35%. How much hash should you get from Tommy for your blow? - Roger Wright
GeneralNeed help about vc++memberZakaria khan7 Oct '03 - 3:52 
hi i am Zakaria khan from Bangladesh. i want to learn visual c++ but i dont know any thing about it. i am also confused because i can't take the decision that from where i should start. Now any one please help me with the most basic code pleaseeeeeeee. Confused | :confused:
GeneralRe: Need help about vc++memberjhwurmbach7 Oct '03 - 4:19 
Zakaria khan wrote:
Now any one please help me with the most basic code pleaseeeeeeee.
 
Sigh | :sigh:
// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int main(int argc, char* argv[])
{
	printf("Hello World!\n");
	return 0;
}

 

Who is 'General Failure'? And why is he reading my harddisk?!?
GeneralNeed HelpmemberMohsin Rizwan6 Oct '03 - 11:42 
HEllo, Um Mohsin From Pakistan, I want creat a project in C++ but i don't know how i creat Password protection and i want that my title run in random colors.. is it possible or any one can make this for me .. plzzz
BYe
GeneralRe: Need HelpmemberJubjub6 Oct '03 - 21:42 
OMG | :OMG:
GeneralRange of an enumeratormemberGavin Greig6 Oct '03 - 1:50 
On the whole, I agree with the points of the article, but I think there may be a bit of a potential "gotcha" with enums too, which it's worth bringing to people's attention.
 
Valid values of an enum are not quite restricted to the values that are explicitly listed; they are in fact restricted to the range of the enum, which holds all the enumerator values rounded up to the nearest larger binary power, minus 1. The example Stroustrup gives is:
 
enum flag {x = 1, y = 2, z = 4, e = 8};  // range 0:15
If you want to cast an int so that it has the enumerated type "flag", then the cast will succeed for an integer in the range 0 to 15, even though not all of those values have been declared within the enum.
 
This problem would still occur with enums where the values are implicitly assigned: an enum with nine implicitly assigned members would still have a range of 0 to 15.
 
Enums are better than a const int, but they're still not proof against the occasional faux pas.
 
Gavin Greig
 
"Haw, you're no deid," girned Charon. "Get aff ma boat or ah'll report ye."
Matthew Fitt - The Hoose O Haivers: The Twelve Trauchles O Heracles.

GeneralCBClass::constsOfInterestToThisClass var;sussAnonymous12 Jul '03 - 6:37 

// How could you declare this without errors?
constsOfInterestToThisClass var;
 
// Should be: ?!
CBClass::constsOfInterestToThisClass var;
 

 
Greetings
GeneralRe: CBClass::constsOfInterestToThisClass var;editorRob Manderson13 Jul '03 - 11:41 
Fixed!
 
Rob Manderson
 
http://www.mindprobes.net
GeneralWhat's the size of...memberKochise8 Jul '03 - 23:46 
...constsOfInterestToThisClass ?
 
In my code, as I have only few values and want to limit the size of 'constsOfInterestToThisClass', I would like to use a char, instead of a int Wink | ;) Tell me how to do...
 
constsOfInterestToThisClass var;
short siz;
size = sizeof(var);
 
Kochise
 
In Cod we trust !
GeneralI think...memberBrian Delahunty3 Jul '03 - 6:07 
... that people are missing the point with the article Rob... They are commenting on the OOP/D side fo things when the article was about const's and enum's. Not your fault though. Big Grin | :-D
 
Regards,
Brian Dela Smile | :)
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 6 Oct 2003
Article Copyright 2003 by Rob Manderson
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid