tags:

views:

251

answers:

6

We have a template conversion function intended for use with numeric datatypes. Inside it contains a construct that makes it not compile with types like pointers.

template<class To, class From>
To ConvertTo( From what )
{
    assert( 2 * what == what * 2 ); // this will not compile for pointers
    //skipped
}

This function compiles and works allright when a enum is passed as the second template parameter:

enum TSomeEnum {
   SE_First,
   SE_Second       
};

TSomeEnum enumValue = SE_First;
int result = ConvertTo<int>( enumValue );

The above code compiles and runs as intended on VC++7.

How does the operation * work for enums? Is it a way to undefined behaviour?

+4  A: 

Enums degrade to ints (old C feature) so that why this is working. I don't think it's undefined behaviour although it might be the sort of behaviour you don't want/expect.

Timo Geusch
Enum's degrade to some underlying integral type that is capable of storing all values of the enum. Degrate to 'int' is not correct for C++.
Richard Corden
A: 

enums are stored as integers, and can even be cast to integers.

The compiler probably stored your above enum as

enum TSomeEnum {
   SE_First = 0,
   SE_SEcond = 1
};

I'm not sure if this numbering system is part of the C/C++ standard or just how compilers do it, but I've always seen them in order.

Anyway, to answer your question, since they're stored as ints underneath, it should be able to be multiplied. Some compilers may give you a warning about type conversion though.

teeks99
See my comments re "integer" to the other two posts. Re the values, the standard guarantees that the first enumerator (without an explicit intialization) will have value zero. Each subsequent enumerator has a value of the previous enumerator +1 unless of course you specify an explicit value.
Richard Corden
+2  A: 

Enums are promoted to integers for arithmetic operations.

The values of enums are always numbered up from zero (this is part of the standard), but you can specify whatever values you like. Automatic numbering then procedes incrementally from the last item you explicitly numbered. The compiler uses the smallest integral type that contains all of the values of an enum to store it.

eg:

enum foo
{
    VALUE0, /* = 0 */
    VALUE1, /* = 1 */
    VALUE3 = 1234,
    VALUE4  /* = 1235 */
};
James
Enums have an underlying representation. This might be 'signed char' in which case, yet they will be promoted to int, however, the underlying type may also be unsigned int and so no promotion takes place.
Richard Corden
+1  A: 

Timo described why it works.

For a safer test, you could use boost::TypeTraits

peterchen
+2  A: 

I think peterchen has the ideal answer, but if boost is too much for you, change your current version to:

template<class To, class From>
To ConvertTo( From what, From checkConvertableToInt = 2 )
{
}

C++ doesn't allow implicit conversions from integers to enumerations. Similarly, it's only possible to implicitly convert the null pointer constant (0) to a pointer type.

Some of the other answers refer to an enumeration being implemented as an int or being promoted to an int. This is not true. In C++, an enumeration is defined to have an underlying type that is capable of storing all values in the enumeration. The following enumeration will therefore have an underlying type of 'unsigned int' or a larger signed/unsigned integral type:

enum E {
  E0
  , E1=MAX_INT+1u
};

The enumeration then follows the rules for its underlying type when you use it in an operation.

Richard Corden
+1 for using assignment to test for the enum, however be careful of assertions with side effects.
iain
@Iain: Thanks for catching that, my change completely modified the behaviour of the original program. I've updated the example now to have the assignment as part of a default argument. No need to use the assert at all.
Richard Corden
@Richard nice!!
iain
+1  A: 

Richard Corden's answer gives a good mechanism to force enums to fail in the assert, however the side effect can cause problems, as you cannot be sure the code inside the assert will always run.

This test should probably by placed in a generic function to avoid the side effects.

template<class To, class From>
To ConvertTo( From what )
{
    assert( testIntegralType(what) );
}

template<class T>
void testIntegralType( T val )
{
    T v1 = val;
    assert( 2*v1 == 2*val );
}
iain