本ドキュメントでは、src/main.cpp で使用している C++17 の主要機能について、ソースコードを引用しながら解説します。
- constexpr if
- 構造化束縛 (Structured Bindings)
- if/switch 文での初期化子
- constexpr ラムダ
- 畳み込み式 (Fold Expressions)
- inline 変数
- クラステンプレートの引数推論 (CTAD)
- ネストされた名前空間
- auto テンプレートパラメータ
- 属性 (Attributes)
コンパイル時に条件分岐を評価する機能です。
テンプレートプログラミングにおいて、型に応じて異なる処理を行う場合に非常に便利です。従来の 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の分岐はインスタンス化されない (コンパイルエラーにならない) - テンプレートメタプログラミングが大幅に簡潔になる
構造体、配列、タプルなどを分解して個別の変数に代入する機能です。
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などに適用可能 - コードの可読性が向上する
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)
ラムダ式をコンパイル時に評価できる機能です。
constexpr auto square = [](int x) constexpr { return x * x; };使用例:
constexpr int sq5 = square(5);
printf(" square(5) = %d\n", sq5); // 25ポイント:
- ラムダ式に
constexprを指定できる - コンパイル時定数として使用可能
static_assertやテンプレート引数にも使える
可変長テンプレート引数に対して演算を適用する機能です。
従来の再帰的なテンプレート展開が不要になります。
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) |
初期値付き左畳み込み |
ヘッダファイルで変数を定義できる機能です。
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なメンバ変数もクラス内で初期化可能に- テンプレートの静的メンバにも適用可能
クラステンプレートのインスタンス化時に、テンプレート引数を省略できる機能です。
コンストラクタの引数から自動的に型が推論されます。
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などでも同様に使用可能- カスタムの推論ガイドも定義できる
名前空間のネストを簡潔に書ける機能です。
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;
}テンプレートの非型パラメータに auto を使用できる機能です。
型を明示的に指定する必要がなくなります。
template<auto Value>
struct Constant {
static constexpr auto value = Value;
};使用例:
printf(" Constant<100>::value = %d\n", Constant<100>::value);ポイント:
- 整数、ポインタ、列挙型など様々な非型パラメータに対応
- 型に依存しない汎用的なテンプレートが書ける
コンパイラに追加情報を伝えるための標準化された構文です。
未使用の変数やパラメータに対する警告を抑制します。
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
[[maybe_unused]] int unused_var = 0;
// ...
}戻り値を無視すると警告を出します。
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 変数 - ヘッダオンリーライブラリの作成
- 属性 - コンパイラ警告の制御