User Defined C++11 enum class Default Constructor

A type defined with enum class or enum struct is not a a class but a scoped enumeration and can not have a default constructor defined. The C++11 standard defines that your PinID pid = PinID(); statement will give a zero-initialization. Where PinID was defined as a enum class. It also allows enum types in general to hold values other than the enumerator constants.

To understand that PinID() gives zero initialization requires reading standard sections 3.9.9, 8.5.5, 8.5.7 and 8.5.10 together:

8.5.10An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized

8.5.7To value-initialize an object of type T means:otherwise, the object is zero-initialized.

8.5.5To zero-initialize an object or reference of type T means: — if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;

3.9.9 – States that enumeration types are part of the set of types known as scalar types.

A possible solution:

To meet your points 1 to 5 you could write a class along the lines of:

class PinID
{
private:
    PinID(int val)
    : m_value(val)
    {}

    int m_value;

public:
    static const PinID N4;
    static const PinID N17;
    /* ...etc... */ 

    PinID() 
    : m_value(N4.getValue())
    {}

    PinID(const PinID &id)
    : m_value(id.getValue())
    {}

    PinID &operator = (const PinID &rhs)
    {
        m_value = rhs.getValue();
        return *this;
    }

    int getValue() const
    {
        return m_value;
    }

    // Attempts to create from int and throw on failure.
    static PinID createFromInt(int i);

    friend std::istream& operator>>(std::istream &is, PinID &v)
    {
        int candidateVal(0);
        is >> candidateVal;
        v = PinID::createFromInt(candidateVal);
        return is;
    }
};

const PinID PinID::N4 = PinID(4);
/* ...etc... */

That can give you something that you would have to make specific efforts to get an invalid values into. The default constructor and stream operator should allow it to work with lexical_cast.

Seems it depends how critical the operations on a PinID are after it’s creation whether it’s worth writing a class or just handling the invalid values everywhere as the value is used.

Leave a Comment