Skip to content

Latest commit

 

History

History
333 lines (240 loc) · 8.16 KB

File metadata and controls

333 lines (240 loc) · 8.16 KB

C++17 機能解説

本ドキュメントでは、src/main.cpp で使用している C++17 の主要機能について、ソースコードを引用しながら解説します。


目次

  1. constexpr if
  2. 構造化束縛 (Structured Bindings)
  3. if/switch 文での初期化子
  4. constexpr ラムダ
  5. 畳み込み式 (Fold Expressions)
  6. inline 変数
  7. クラステンプレートの引数推論 (CTAD)
  8. ネストされた名前空間
  9. auto テンプレートパラメータ
  10. 属性 (Attributes)

1. constexpr if

コンパイル時に条件分岐を評価する機能です。

テンプレートプログラミングにおいて、型に応じて異なる処理を行う場合に非常に便利です。従来の SFINAE や std::enable_if よりも直感的に書けます。

template<typename T>
constexpr auto type_name() {
    if constexpr (sizeof(T) == 1) {
        return "byte";
    } else if constexpr (sizeof(T) == 2) {
        return "word";
    } else if constexpr (sizeof(T) == 4) {
        return "long";
    } else {
        return "unknown";
    }
}

使用例:

printf("  int8_t  type: %s\n", type_name<int8_t>());   // "byte"
printf("  int16_t type: %s\n", type_name<int16_t>()); // "word"
printf("  int32_t type: %s\n", type_name<int32_t>()); // "long"

ポイント:

  • if constexpr の条件はコンパイル時に評価される
  • 条件が false の分岐はインスタンス化されない (コンパイルエラーにならない)
  • テンプレートメタプログラミングが大幅に簡潔になる

2. 構造化束縛 (Structured Bindings)

構造体、配列、タプルなどを分解して個別の変数に代入する機能です。

struct Point {
    int16_t x;
    int16_t y;
};

constexpr Point make_point(int16_t x, int16_t y) {
    return Point{x, y};
}

使用例:

auto [px, py] = make_point(100, 200);
printf("  Point: x=%d, y=%d\n", px, py);

ポイント:

  • auto [変数1, 変数2, ...] = 式; の形式で使用
  • 構造体のメンバ、配列の要素、std::tuple などに適用可能
  • コードの可読性が向上する

3. if/switch 文での初期化子

if 文や switch 文の中で変数を初期化できる機能です。

変数のスコープを制限し、より安全なコードが書けます。

int check_value(int val) {
    if (int doubled = val * 2; doubled > 100) {
        return doubled;
    } else {
        return val;
    }
}

使用例:

printf("  check_value(30) = %d\n", check_value(30));  // 30  (60 <= 100)
printf("  check_value(60) = %d\n", check_value(60));  // 120 (120 > 100)

ポイント:

  • if (初期化; 条件) の形式
  • 初期化した変数は if 文のスコープ内でのみ有効
  • switch 文でも同様に使用可能: switch (auto x = get_value(); x)

4. constexpr ラムダ

ラムダ式をコンパイル時に評価できる機能です。

constexpr auto square = [](int x) constexpr { return x * x; };

使用例:

constexpr int sq5 = square(5);
printf("  square(5) = %d\n", sq5);  // 25

ポイント:

  • ラムダ式に constexpr を指定できる
  • コンパイル時定数として使用可能
  • static_assert やテンプレート引数にも使える

5. 畳み込み式 (Fold Expressions)

可変長テンプレート引数に対して演算を適用する機能です。

従来の再帰的なテンプレート展開が不要になります。

template<typename... Args>
constexpr auto sum(Args... args) {
    return (args + ...);  // 右畳み込み
}

使用例:

constexpr int total = sum(1, 2, 3, 4, 5);
printf("  sum(1,2,3,4,5) = %d\n", total);  // 15

畳み込みの種類:

展開結果
(args + ...) 右畳み込み: a1 + (a2 + (a3 + a4))
(... + args) 左畳み込み: ((a1 + a2) + a3) + a4
(args + ... + init) 初期値付き右畳み込み
(init + ... + args) 初期値付き左畳み込み

6. inline 変数

ヘッダファイルで変数を定義できる機能です。

ODR (One Definition Rule) 違反を起こさずに、ヘッダのみで定数を定義できます。

inline constexpr int VERSION_MAJOR = 1;
inline constexpr int VERSION_MINOR = 0;

使用例:

printf("Version: %d.%d\n", VERSION_MAJOR, VERSION_MINOR);

ポイント:

  • 複数の翻訳単位で同じ変数定義を共有できる
  • static なメンバ変数もクラス内で初期化可能に
  • テンプレートの静的メンバにも適用可能

7. クラステンプレートの引数推論 (CTAD)

クラステンプレートのインスタンス化時に、テンプレート引数を省略できる機能です。

コンストラクタの引数から自動的に型が推論されます。

template<typename T>
struct Wrapper {
    T value;
    constexpr Wrapper(T v) : value(v) {}
    constexpr T get() const { return value; }
};

使用例:

Wrapper w{42};  // Wrapper<int> が推論される
printf("  Wrapper value: %d\n", w.get());

ポイント:

  • Wrapper<int> w{42}; と書く必要がなくなった
  • std::pair, std::tuple などでも同様に使用可能
  • カスタムの推論ガイドも定義できる

8. ネストされた名前空間

名前空間のネストを簡潔に書ける機能です。

namespace x68k::demo::util {
    constexpr int magic_number = 0x4D44;  // "MD" in ASCII
}

使用例:

printf("  x68k::demo::util::magic_number = 0x%04X\n",
       x68k::demo::util::magic_number);

従来の書き方との比較:

// C++14 以前
namespace x68k {
    namespace demo {
        namespace util {
            constexpr int magic_number = 0x4D44;
        }
    }
}

// C++17
namespace x68k::demo::util {
    constexpr int magic_number = 0x4D44;
}

9. auto テンプレートパラメータ

テンプレートの非型パラメータに auto を使用できる機能です。

型を明示的に指定する必要がなくなります。

template<auto Value>
struct Constant {
    static constexpr auto value = Value;
};

使用例:

printf("  Constant<100>::value = %d\n", Constant<100>::value);

ポイント:

  • 整数、ポインタ、列挙型など様々な非型パラメータに対応
  • 型に依存しない汎用的なテンプレートが書ける

10. 属性 (Attributes)

コンパイラに追加情報を伝えるための標準化された構文です。

[[maybe_unused]]

未使用の変数やパラメータに対する警告を抑制します。

int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
    [[maybe_unused]] int unused_var = 0;
    // ...
}

[[nodiscard]]

戻り値を無視すると警告を出します。

class Counter {
public:
    [[nodiscard]] int get() const { return count_; }
private:
    int count_;
};
counter.get();  // 警告: 戻り値が無視されている
int x = counter.get();  // OK

その他の C++17 属性:

  • [[fallthrough]] - switch 文で意図的なフォールスルーを示す

まとめ

C++17 はコードの可読性と安全性を大幅に向上させる機能を多数導入しました。X68000 のような制約のある環境でも、例外や RTTI を使わない範囲であれば、これらのモダンな機能を活用できます。

特に以下の機能は組み込み・レトロ環境でも有用です:

  • constexpr if - コンパイル時の条件分岐
  • 構造化束縛 - コードの可読性向上
  • 畳み込み式 - 可変長引数の簡潔な処理
  • inline 変数 - ヘッダオンリーライブラリの作成
  • 属性 - コンパイラ警告の制御