列挙型 解答ページ | Programming Place Plus 新C++編

トップページ新C++編列挙型

このページの概要

このページは、練習問題の解答例や解説のページです。



解答・解説

問題1 (確認★)

次の列挙型で、列挙子の値はそれぞれいくつですか?

enum class Fruit {
    apple,
    banana,
    melon = 10,
    orange,
    strawberry = -10,
    watermelon,
};


最初の列挙子の値は、= を使って明示しなければ 0 であると決まっています(本編解説)。したがって、apple は 0 です。

2つ目以降の列挙子は、= を使って明示しなければ、手前の列挙子の値に +1 したものになります(本編解説)。banana は、apple の値 +1 なので、1 ということになります。

melon は明示的に 10 を与えているので、これは指定通り 10 です。

orange は melon の次なので、10 + 1 で 11 です。

strawberry は明示的に -10 を与えているので、-10 です。

watermelon は strawberry の値 +1 なので、-9 になります。

問題2 (確認★)

次の中から、エラーになるものをすべて選んでください。

  1. scoped enum型の変数に 10 を代入する
  2. scoped enum と int型の値を比較する
  3. unscoped enum型の変数を、その基底型の変数に代入する
  4. int型の値を unscoped enum型の変数に代入する
  5. scoped enum と unscoped enum を比較する


1番はコンパイルエラーです。整数型から scoped enum には暗黙的には変換されず、static_cast が必要です(本編解説)。

2番はコンパイルエラーです。整数型から scoped enum に暗黙的に変換されないですし、scoped enum から整数型にも暗黙的に変換されません(本編解説)。これもstatic_cast が必要です。

3番は問題ありません。unscoped enum から整数型へは暗黙的に変換できます(本編解説)。

4番はコンパイルエラーです。整数型から unscoped enum へは暗黙的に変換されず、static_cast が必要です(本編解説)。

5番はコンパイルエラーです。scoped enum はほかの型に暗黙的に変換されることがないため、やはり static_cast をしない限りは比較できません。

問題3 (基本★★)

トランプのマークを表現する列挙型の値を渡すと、その色を返す関数を作成してください。色についても列挙型で表現するようにしてください。

マークと色の関係は、スペードとクラブが黒、ダイヤとハートが赤とします。


特に事情がなければ scoped enum を使いましょう。次のように実装できます。

#include <iostream>
#include <string>

// カードのマーク
enum class CardMark {
    spade,
    club,
    diamond,
    heart,
};

// カードのマークの色
enum class CardMarkColor {
    black,
    red,
};

// カードのマークの色を返す
// card_mark: カードのマーク
// 戻り値: card_mark に対応する色
CardMarkColor get_mark_color(CardMark card_mark)
{
    if (card_mark == CardMark::spade || card_mark == CardMark::club) {
        return CardMarkColor::black;
    }
    else {
        return CardMarkColor::red;
    }
}

// カードのマークの色に応じた文字列表現を返す
// card_mark_color: カードのマークの色
// 戻り値: card_mark_color を文字列化した結果
std::string get_mark_color_string(CardMarkColor card_mark_color)
{
    switch (card_mark_color) {
    case CardMarkColor::black:
        return "black";
    case CardMarkColor::red:
        return "red";
    default:
        return "";
    }
}

int main()
{
    auto spade_color = get_mark_color(CardMark::spade);
    auto club_color = get_mark_color(CardMark::club);
    auto diamond_color = get_mark_color(CardMark::diamond);
    auto heart_color = get_mark_color(CardMark::heart);

    std::cout << get_mark_color_string(spade_color) << "\n";
    std::cout << get_mark_color_string(club_color) << "\n";
    std::cout << get_mark_color_string(diamond_color) << "\n";
    std::cout << get_mark_color_string(heart_color) << "\n";
}

実行結果:

black
black
red
red

問題4 (応用★★★)

次のような関数は分かりづらいといえます。列挙型を使って改善してください。

// 物体の位置を移動させる。
// pos: 元の位置
// isUp: true を指定すると上へ、false を指定すると下へ移動する
// isFast: true を指定すると高速に移動、false を指定すると通常の速度で移動する
// 戻り値: 移動後の位置
Position move(const Position& pos, bool isUp, bool isFast);


分かりづらい理由は、呼び出し方を考えてみると分かります。たとえば、次の2つの呼び出しは何を意味しているでしょう?

pos = move(pos, true, false);
pos = move(pos, false, true);

呼び出し元のコードだけでは2つの bool値の意味が不明瞭です。このコードだけで、1行目が上への移動を意味しており、2行目が下への高速移動を意味していることは普通読み取れません。もしかすると、2つとも false を指定した場合、「移動しない」という意味に感じる人もいるかもしれません(実際には下へ移動する)。

そこで、次のように列挙型を定義して、引数をその型に変更します。

enum class MoveDirection {
    up,
    down,
};

enum class MoveSpeed {
    normal,
    fast,
};

// 物体の位置を移動させる。
// pos: 元の位置
// direction: 移動させる方向
// speed: 移動の速さ
// 戻り値: 移動後の位置
Position move(const Position& pos, MoveDirection direction, MoveSpeed speed);

こうすると、呼び出し元は次のようになります。

pos = move(pos, MoveDirection::up, MoveSpeed::normal);
pos = move(pos, MoveDirection::down, MoveSpeed::fast);

記述は面倒になるかもしれませんが、意味は非常に明確になります。

プログラム全体はこうなります。

#include <iostream>
#include <string>

// 位置
struct Position {
    int x;
    int y;
};

// 移動の方向
enum class MoveDirection {
    up,
    down,
};

// 移動の速さ
enum class MoveSpeed {
    normal,
    fast,
};

// 物体の位置を移動させる。
// pos: 元の位置
// direction: 移動させる方向
// speed: 移動の速さ
// 戻り値: 移動後の位置
Position move(const Position& pos, MoveDirection direction, MoveSpeed speed)
{
    Position new_pos = pos;
    int v {speed == MoveSpeed::fast ? 12 : 4};

    switch (direction) {
    case MoveDirection::up:
        new_pos.y -= v;
        break;
    case MoveDirection::down:
        new_pos.y += v;
        break;
    default:
        break;
    }

    return new_pos;
}

int main()
{
    Position pos {10, 20};

    pos = move(pos, MoveDirection::up, MoveSpeed::normal);
    std::cout << pos.x << ", " << pos.y << "\n";

    pos = move(pos, MoveDirection::down, MoveSpeed::fast);
    std::cout << pos.x << ", " << pos.y << "\n";
}

実行結果:

10, 16
10, 28


参考リンク



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る