コンテンツにスキップ

C++/ムーブキャプチャ

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

はじめに

[編集]

この章では、C++のラムダ式におけるムーブキャプチャの概念とその使い方について学びます。ムーブキャプチャは、リソース管理の効率化やパフォーマンス向上に貢献する重要な技術です。本章を通じて、ムーブキャプチャの基本から応用例までを理解し、実際のプログラムで活用できるようになることを目指します。

基本概念の復習

[編集]

ムーブキャプチャを理解するためには、ラムダ式とムーブセマンティクスについての基本を知っておくことが大切です。

ラムダ式とは

[編集]

ラムダ式は、匿名関数とも呼ばれ、コード内で簡潔に関数を定義できる構文です。基本的な例は次の通りです:

auto lambda = [](int x) { return x + 1; };

キャプチャの基本

[編集]

ラムダ式は、外部の変数をキャプチャすることができます。キャプチャには、値キャプチャと参照キャプチャの2種類があります。

  • 値キャプチャ ([=]):外部の変数をコピーしてキャプチャします。
  • 参照キャプチャ ([&]):外部の変数を参照でキャプチャします。

初期化キャプチャとは

[編集]

C++14で導入された初期化キャプチャは、ラムダ式内で新しい変数を作成・初期化し、それを使用できる機能です。

int x{10};
auto lambda = [v = x + 1]() {
    std::cout << v << std::endl;
};
lambda();  // 11

この機能は特に、キャプチャリストで変数を初期化したい場合や、外部変数を変換して使用したい場合に有効です。

ムーブセマンティクスの復習

[編集]

C++11で導入されたムーブセマンティクスは、オブジェクトのリソースを効率的に移動するための機能です。これにより、リソースを再利用しつつパフォーマンスを向上させることができます。

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1);  // v1のリソースをv2に移動

ムーブキャプチャとは

[編集]

ムーブキャプチャを使用すると、ムーブオンリーのオブジェクト(例:std::unique_ptr)をラムダ式内でキャプチャすることができます。

ムーブキャプチャの構文

[編集]

ムーブキャプチャはキャプチャリストでstd::moveを明示的に使うことで行います。

auto ptr = std::make_unique<int>(10);
auto lambda = [ptr = std::move(ptr)]() {
    std::cout << *ptr << std::endl;
};

ムーブキャプチャの使い方

[編集]

ムーブキャプチャの使い方について、いくつかの例を紹介します。

基本的な構文

[編集]

ムーブオンリーオブジェクトをラムダ式にムーブキャプチャする例です。

#include <iostream>
#include <memory>

void example() {
    auto ptr = std::make_unique<int>(42);
    auto lambda = [ptr = std::move(ptr)]() {
        std::cout << *ptr << std::endl;
    };
    lambda();  // 42
}

スレッドでのムーブキャプチャ

[編集]

非同期処理やスレッドを使用する場合にも、ムーブキャプチャは有効です。

#include <iostream>
#include <memory>
#include <thread>

void threadExample() {
    auto ptr = std::make_unique<int>(456);
    std::thread t([ptr = std::move(ptr)]() {
        std::cout << *ptr << std::endl;
    });
    t.join();  // 456
}

パフォーマンスと最適化

[編集]

ムーブキャプチャを利用すると、オブジェクトのコピーを避けてリソースの所有権を移動できるため、効率的なリソース管理が可能になります。

パフォーマンス比較

[編集]

ムーブキャプチャとコピーキャプチャのパフォーマンスを比較した例です。

#include <iostream>
#include <vector>
#include <chrono>

void performanceComparison() {
    std::vector<int> largeData(1000000, 1);

    // コピーキャプチャ
    auto startCopy = std::chrono::high_resolution_clock::now();
    auto copyLambda = [largeData]() {
        return largeData.size();
    };
    std::cout << "Copy size: " << copyLambda() << std::endl;
    auto endCopy = std::chrono::high_resolution_clock::now();

    // ムーブキャプチャ
    auto startMove = std::chrono::high_resolution_clock::now();
    auto moveLambda = [largeData = std::move(largeData)]() {
        return largeData.size();
    };
    std::cout << "Move size: " << moveLambda() << std::endl;
    auto endMove = std::chrono::high_resolution_clock::now();

    auto copyDuration = std::chrono::duration_cast<std::chrono::microseconds>(endCopy - startCopy).count();
    auto moveDuration = std::chrono::duration_cast<std::chrono::microseconds>(endMove - startMove).count();

    std::cout << "Copy duration: " << copyDuration << "us" << std::endl;
    std::cout << "Move duration: " << moveDuration << "us" << std::endl;
}

トラブルシューティングとベストプラクティス

[編集]

ムーブキャプチャを使用する際に気をつけるべき点と解決策を紹介します。

一般的な問題

[編集]
  • キャプチャ後に元のオブジェクトを参照してクラッシュする。
  • ムーブキャプチャしたオブジェクトがラムダ式のスコープ外で破棄される。

ベストプラクティス

[編集]
  • キャプチャ後は元のオブジェクトを参照しない。
  • キャプチャするオブジェクトは、ラムダ式外で不要になっていることを確認する。
  • キャプチャリストを明示的に書いて意図しないキャプチャを防ぐ。

まとめ

[編集]

ムーブキャプチャは、オブジェクトのリソース管理を効率化し、プログラムのパフォーマンスを向上させます。ムーブキャプチャの基本を理解し、正しく活用することで、より効率的なC++プログラムを作成できるようになります。