コンテンツにスキップ

最終更新:2026-10-12

C++ ref と reference_wrapper

C++における std::cref、std::ref、std::reference_wrapper の深入理解

C++プログラミングでは、コピーを行わずに参照を渡したいことや、参照が必要な場所で定数オブジェクトを使いたいことがあります。これらの問題を解決するため、C++標準ライブラリは3つの有用なツールを提供しています:std::cref、std::ref そして std::reference_wrapper。この記事では、これらのツールの用途、違い、そして実際の応用を深く掘り下げます。

また、Rust言語ではよくUnwrapメソッドが実装されていますが、C++ではどのように実装すればよいのでしょうか?

ここでApache arrowのコードを参考にします。次のような一節があります:

std::optional<std::reference_wrapper<const TransportStatusDetail>>
TransportStatusDetail::Unwrap(const Status& status) {
  std::shared_ptr<StatusDetail> detail = status.detail();
  if (!detail) return std::nullopt;
  if (detail->type_id() != kTypeId) return std::nullopt;
  return std::cref(arrow::internal::checked_cast<const TransportStatusDetail&>(*detail));
}

その後、このように呼び出すことで、Statusの中の実際のTransportStatusDetailを取り出すことができます。

Status status(transportDetail);
auto result = Unwrap(status);

以下、いくつかの側面から見ていき、最後にDemoコード全体を示します。

1. std::cref:定数参照を作成する

std::cref はテンプレート関数で、定数オブジェクトへの参照を作成するために使われます。これは std::reference_wrapper オブジェクトを返し、参照が必要な場所で使用できます。これは関数の引数渡しで特に便利です。コピーを行わずに定数オブジェクトを渡しながら、参照のセマンティクスを保てるからです。

例:

#include <iostream>
#include <functional>

void printValue(const int& value) {
    std::cout << "Value: " << value << std::endl;
}

int main() {
    int number = 42;
    auto crefNumber = std::cref(number);

    printValue(crefNumber);  // 使用常量引用传递参数

    return 0;
}

2. std::ref:変更可能な参照を作成する

std::cref とは異なり、std::ref はテンプレート関数で、変更可能なオブジェクトへの参照を作成するために使われます。これは std::reference_wrapper オブジェクトを返し、参照が必要な場所での使用を許可すると同時に、参照されるオブジェクトの変更も許可します。

例:

#include <iostream>
#include <functional>

void modifyValue(int& value) {
    value *= 2;
}

int main() {
    int number = 42;
    auto refNumber = std::ref(number);

    modifyValue(refNumber);  // 使用可修改的引用作为参数

    std::cout << "Modified Value: " << number << std::endl;

    return 0;
}

3. std::reference_wrapper:参照のラッパー

std::reference_wrapper はテンプレートクラスで、参照をラップして、コンテナに格納したり参照の形で渡したりできるようにします。参照に似た構文を提供し、コンテナは参照を直接格納できないため、標準コンテナと一緒に使うことができます。

例:

#include <iostream>
#include <vector>
#include <functional>

int main() {
    int number1 = 42;
    int number2 = 73;

    std::vector<std::reference_wrapper<int>> numbers = {std::ref(number1), std::ref(number2)};

    for (auto& num : numbers) {
        num.get() += 10;  // 修改原始对象的值
    }

    std::cout << "Number 1: " << number1 << std::endl;
    std::cout << "Number 2: " << number2 << std::endl;

    return 0;
}

この例では、std::reference_wrapper によって参照をコンテナの中にラップし、get()メソッドを使って元のオブジェクトの値にアクセスして変更できるようになっています。

4.Unwrap Demo

ここで具体的なコード断片を示します。完全なコードは知識星球の内容を参照してください。

std::optional<std::reference_wrapper<const TransportStatusDetail>> Unwrap(const Status& status) {
    std::shared_ptr<StatusDetail> detail = status.detail();
    if (!detail) return std::nullopt;
    if (detail->type_id() != kTypeId) return std::nullopt;
    return std::cref(static_cast<const TransportStatusDetail&>(*detail));
}

int main() {
    // 创建 TransportStatusDetail 对象
    std::shared_ptr<StatusDetail> transportDetail = std::make_shared<TransportStatusDetail>();

    // 创建 Status 对象并传入 TransportStatusDetail 对象
    Status status(transportDetail);

    auto result = Unwrap(status);

    if (result) {
        const TransportStatusDetail& detail = result.value().get();
        // 使用 detail 进行操作
        std::cout << "TransportStatusDetail found." << std::endl;
    } else {
        std::cout << "No valid TransportStatusDetail found." << std::endl;
    }

    return 0;
}

C++那些事について一緒に議論しましょう。より多くのプロジェクト/コンテンツについては、知識星球への参加を歓迎します。

関連トピック

コメント