コンテンツにスキップ

最終更新:2026-10-12

C++17 17の高度な機能

C++17の高度な機能17選を全面的に総覧する

C++17は現在最もよく使われているバージョンの1つです。今日は時間をかけて17の重要な機能を整理します。すべての機能はこれだけにとどまりません。

1. 並列アルゴリズム

C++17は、標準ライブラリの多くのアルゴリズムに並列版を導入しました。これらのアルゴリズムは並列に実行できるため、マルチコアシステムで顕著な性能向上をもたらす可能性があります。

以前、この機能を全面的に紹介した記事を書いたので、そちらをご覧ください:

例:

#include <algorithm>
#include <vector>
#include <execution>
int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::sort(std::execution::par, v.begin(), v.end());
}

この例では、std::sortは並列に実行され、ベクトルvの要素を並列方式でソートします。

2. If Initializers

C++17のif初期化子は、if文の中で直接変数を初期化できる機能です。この初期化の方法は、コードの可読性と簡潔さをある程度高めます。

従来のC++では、通常このように変数を初期化していました:

int x;
if (condition) {
    x = 42;
} else {
    x = 24;
}

一方C++17では、if初期化子を使ってこのプロセスを簡潔にし、コードをよりコンパクトにできます:

if (bool condition = /* some condition */) {
    int x = 42;
} else {
    int x = 24;
}

この例では、変数xの初期化をif文の中に直接記述しています。変数conditionはif文の中で定義・初期化され、その後if文のブロック内で使えます。この方法はより直感的で簡潔であり、特に単純な条件付き初期化の際に有効です。

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

CTADにより、コンパイラは関数引数からテンプレート引数を自動的に推論します。これにより、テンプレート引数を明示的に指定しなくても、テンプレートをより簡単に使えるようになります。

以前、この機能を全面的に解説した記事を書いています:

例:

template <typename T>
void foo(T t) {
    // ...
}

int main() {
    foo(42);  // 编译器推导出T的类型为int
}

この例では、foo(42)を呼び出すとき、コンパイラはTの型がintであると推論します。

4. template <auto>

テンプレートキーワードが、非型テンプレートパラメータのプレースホルダとして導入されました。これにより、テンプレートの中で任意の型の値を表せるようになります。

例:

template<auto value>
struct constant {
    static constexpr auto get_value() { return value; }
};

// 用法
static_assert(constant<42>::get_value() == 42);

5. std::optional と std::variant

std::optional と std::variant はC++17で導入された2つの新しい型です。std::optional は存在するかもしれないししないかもしれない値を表し、std::variant は型安全な共用体を表し、異なる型の値を保持できます。

例:

#include <optional>
#include <variant>

int main() {
    std::optional<int> opt = 42;
    std::variant<int, double> var = 3.14;
}

この例では、optは値42を含むoptionalな整数で、varは値3.14を含むvariantです。

6. フォールド式

C++17では、フォールド式が、パラメータパックに対して二項演算を実行するための簡潔な方法を提供します。明示的な再帰や反復を使わずに、パラメータパック内の要素の総和・積・連結などの操作を実行できます。

例えば:

#include <iostream>

template<typename T>
T sum(T t) {
    return t;
}

// 使用折叠表达式的递归情况
template<typename T, typename... Args>
T sum(T first, Args... args) {
    return first + sum(args...);
}

int main() {
    int total = sum(1, 2, 3, 4, 5);
    std::cout << "总和: " << total << std::endl;

    return 0;
}

再帰的なsum関数の中のフォールド式(first + ... + args)は、パラメータパックの各要素に加算操作を適用します。

7. 構造化束縛

構造化束縛を使うと、オブジェクトをその構成要素に分解できます。タプルのアンパックに似ています。

過去の記事:

例:

#include <tuple>
#include <string>
int main() {
    std::tuple<int, std::string, double> t(42, "hello", 3.14);
    auto [i, s, d] = t;  // i = 42, s = "hello", d = 3.14
}

この例では、構造化束縛[i, s, d]がタプルtをその構成要素に分解します。

8.テンプレートテンプレートパラメータ

例えば:C++17では、template<template<class...>typename bob> struct foo {} という構文は、bobという名前のテンプレートテンプレートパラメータを受け取る、fooという名前のテンプレートを宣言します。テンプレートテンプレートパラメータ bob 自身は、任意の数のテンプレート型パラメータを受け取ります。

template <template<class...> typename bob>
struct foo {
    template<typename T>
    void bar(const bob<T>& arg) {
        std::cout << "size: " << arg.size() << std::endl;
    }
};

int main() {
    foo<std::vector> f;
    std::vector<int> vec = {1, 2, 3, 4, 5};
    f.bar(vec);

    return 0;
}

main 関数の中で、std::vector を bob のテンプレート引数として foo をインスタンス化しています。これにより、std::vector、std::list、ユーザー定義のテンプレートなど、任意の数の型パラメータを受け取る任意のテンプレートと協働できる、汎用的な構造 foo を作れます。

9. インライン変数

C++17では、クラス定義の内部で変数をインラインとして定義できます。これによりバイナリサイズの削減が可能になり、変数が複数の翻訳単位で重複してコピーされるのを防いで、性能が向上する可能性があります。

例:

class MyClass {
public:
    inline static int inlineVar = 42;
};

int main() {
    int localVar = MyClass::inlineVar;
}

ここで、inlineVarはMyClassのインライン静的メンバ変数です。

10. 属性の改善

C++17は新しい属性を提供するとともに既存の属性を改良し、開発者がコンパイラにコードの動作に関するより多くの情報を与えられるようにしました。

例:

[[nodiscard]] int get_sum(int a, int b) {
    return a + b;
}

int main() {
    auto result = get_sum(1, 2);
    // 编译器可能会警告‘result’未使用
}

この例では、[[nodiscard]]は関数に適用できる属性で、その戻り値が呼び出し側によって捨てられるべきでないことを示します。

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

C++17はフォールド式によって可変引数テンプレートを強化し、パラメータパックを扱うコードをより簡潔で明快にしました。

例:

// 外部命名空间
namespace outer {
    // 内部命名空间
    namespace inner {
        void foo() {
            std::cout << "在内部命名空间中" << std::endl;
        }
    }
}

outer::inner::foo();

ネストされた名前空間の定義は、コードを階層的に整理する方法を提供し、特に大規模プロジェクトにおいて可読性と保守性を高めます。また、より構造化された名前空間の階層を提供することで、名前の衝突を避ける助けにもなります。

12. リテラルの改善

C++17はリテラルを強化しました。整数リテラルと浮動小数点リテラルの改善に加え、trueとfalseのリテラルのサポートも含まれます。

例:

auto num = 123_456; // Underscore in integer literals  
auto pi = 3.1415_f; // Suffix for floating-point literals

13. constexpr Lambda

C++17では、条件を満たすlambda関数をconstexprにできます。コンパイル時評価が必要な文脈で使えるようになります。例えば:

constexpr auto lambda = [](int x) { return x * 2; };
static_assert(lambda(5) == 10);

この例では、lambdaはconstexprなlambdaで、整数xを引数に取り、xの2倍を返します。static_assertは、コンパイル時にlambda(5)の値が10と等しいかどうかを検査します。

14. *thisのキャプチャ

lambdaでの*thisのキャプチャがより簡単になり、包含するオブジェクトのメンバに直接アクセスできるようになりました。

class Foo {
    int data = 42;
public:
    auto member_lambda() {
        return [*this] { std::cout << data << std::endl; };
    }
};

// 使用
Foo f;
f.member_lambda()(); // 输出 "42"

15. 拡張されたif文とswitch文

if文やswitch文の条件は、真偽値条件に限らず、任意の式にできるようになりました。これにより制御フローがより柔軟になります。例えば構造化束縛を使う場合:

if (const auto [it, inserted] = map.insert({"foo", bar}); inserted) {
    // ...
}

この例では、if文はinserted変数が真かどうかを調べますが、条件には構造化束縛による代入も含まれています。

16. 範囲forループの汎化

この改善は、開始イテレータとは異なる型のセンチネルや終了イテレータをサポートします。これにより、ヌル終端された範囲を扱うループなど、類似の場面で役立ちます。例えば:

for (auto it = my_container.begin(); it != my_container.end(); ++it) {
   // ...
}

この例では、my_containerは異なる型の終了イテレータを使うコンテナかもしれませんが、ループは正しく動作します。

17. if constexpr

この機能は、コンパイラがコンパイル時に条件を評価できるようにすることで、より汎用的なコードを実現します。条件が真であればifブロック内のコードがコンパイル済みコードに含まれ、そうでなければ破棄されます。実行時に不要な分岐を取り除けるため、コードを簡潔にできます。例えば:

if constexpr (std::is_same_v<T, int>) {
   // 用于int的特定代码
} else {
   // 用于其他类型的代码
}

この例では、if constexpr文は型Tがintかどうかを調べ、それに応じて適切なコードを含めます。

まとめ

本節はここまでにします。他の内容は次の節で解説します。

関連トピック

コメント