#120 – The third type of class member

The problem

Consider this program:

#include <iostream>

enum class DoorState {
    Open,
    Closed,
    Locked
};

class Door {
private:
    DoorState m_state {};

public:
    Door(DoorState state)
        : m_state { state }
    {
    }

    DoorState state() const{
        return m_state;
    }

    bool isLocked() const{
        return m_state == DoorState::Locked;
    }
};

int main(){
    Door frontDoor { DoorState::Locked };

    if (frontDoor.state() == DoorState::Locked) {
        std::cout << "The door is locked\n";
    }
}

Terminal output:
The door is locked

This works, but DoorState exists outside the Door class although it’s designed to be used with Door.

If only there was a way to embed Door inside the class, thereby making the relationship explicit.

Nested types

So far we’ve covered member variables and methods. Class types support a third type of member: nested types/member types

nested type is a type declared inside another type.

Let’s merge DoorState into Door:

#include <iostream>

class Door {
public:
    enum State {
        Open,
        Closed,
        Locked
    };

private:
    State m_state {};

public:
    Door(State state)
        : m_state { state }
    {
    }

    State state() const{
        return m_state;
    }

    bool isLocked() const{
        return m_state == Locked;
    }
};

int main(){
    Door frontDoor { Door::Locked };

    if (frontDoor.state() == Door::Locked) {
        std::cout << "The door is locked\n";
    }
}

Terminal output:
The door is locked

Now, the class now has three kinds of members:

1. Data members        → m_state
2. Member functions    → state(), isLocked()
3. Member types        → State

Observe that the nested types are defined at the top of your class type. This is mandatory.

This refactored version has a few changes:

  1. Members (isLocked()) do not need to access the nested type (DoorState) via the scope resolution operator ::.
  2. The nested types must be fully defined before it can be used
  3. We access the enumeration as Door::Locked rather than Door::State::Locked
  4. The enum class is changed to an unscoped enum. It’s redundant to leave it as an enum class since now it’s scoped to the class.

Nested type aliases

A class can also define a type alias inside itself.

This is useful when the class wants to expose a meaningful type name.

#include <iostream>
#include <string>
#include <string_view>

class Employee {
public:
    using Id = int;

private:
    Id m_id {};
    std::string m_name {};

public:
    Employee(Id id, std::string_view name)
        : m_id { id }
        , m_name { name }
    {
    }

    Id id() const{
        return m_id;
    }

    const std::string& name() const{
        return m_name;
    }
};

int main(){
    Employee employee { 42, "Ada" };

    Employee::Id id { employee.id() };

    std::cout << employee.name()
              << " has employee ID "
              << id << '\n';
}

Terminal output:
Ada has employee ID 42

The alias is declared inside the class: using Id = int;

Outside the class, it is accessed with the scope resolution operator: Employee::Id

An int could represent anything. Employee::Id says what the integer represents.

Standard-library examples

It is very common for classes in the C++ standard library to make use of nested typedefs. As of the time of writing, std::string defines ten nested typedefs

For example, containers commonly provide names such as:

value_type
size_type
iterator
const_iterator
reference
const_reference

std::vector<int> has a value_type of int:

#include <iostream>
#include <vector>

int main(){
		// Type alias
    using Scores = std::vector<int>;

    Scores::value_type score { 95 };
    Scores::size_type count { 3 };

    std::cout << "Score: " << score << '\n';
    std::cout << "Count: " << count << '\n';
}

Terminal output:
Score: 95
Count: 3

Takeaway

Classes can contain a third type of member in addition to member variables and member functions – member types.

Use nested enums when the enum is intended to be used with a class.