モダンC++とCでの代替手法

C++のラムダとCのコールバックへの文脈の渡し方

このレッスンでは、C++のラムダでローカルなデータをコールバックに持ち込み、Cのvoidポインタで文脈を渡す方法と比べます。

はじめに

ラムダは名前のない関数をその場で作り、周りのローカル変数を「キャプチャ」(capture)して中で使えます。Cのコールバックは自分でデータを持てないので、文脈のデータは明示的に渡す必要があり、たいていはvoidポインタを使います。

大切なポイント

  • C++の抽象化を学ぶ前に、Cでのやり方を理解しましょう。
  • RAIIと値のセマンティクス(value semantics)で、リソースの持ち主を表しましょう。
  • マクロや型変換より、C++の型安全な道具を優先しましょう。

CとC++のコード例

C
実行 →
main.c
#include <stdio.h>

int above(int value, void *context) {
    return value > *(int *)context;
}

int main(void) {
    int values[] = {2, 7, 4, 9};
    int threshold = 5;
    int count = 0;
    for (int index = 0; index < 4; ++index) {
        count += above(values[index], &threshold);
    }
    printf("%d\n", count);
}
実行結果
2
C++
実行 →
main.cpp
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector values{2, 7, 4, 9};
    int threshold = 5;
    auto above = [threshold](int value) {
        return value > threshold;
    };
    std::cout << std::count_if(values.begin(), values.end(), above)
              << '\n';
}
実行結果
2

CとC++の比較

Cの関数aboveは基準値を引数void *contextで受け取り、比較の前にint *へ型変換しなければなりません。C++のラムダは変数thresholdを値でキャプチャするので、手作業の型変換なしで型の情報を保てます。どちらのプログラムも、5より大きい値を2個数えます。

C

Cは名前付けの習慣、コールバック、マクロ、文脈を持つstructを使います。

C++

C++はスコープがはっきりした型安全な抽象化を用意しており、実行時のコストはふつうかかりません。

発展問題

両方のバージョンを実行し、コードを書き換えて、それぞれの言語がもたらす安全性の違いを観察しましょう。

  • まずCで仕組みを書き、その後で対応するC++の機能を使いましょう。
  • 手作業のリソース解放をRAIIに置き換えましょう。
  • その機能がメモリの確保や仮想関数の呼び出しを生むかどうかを確認しましょう。

チャレンジで練習する

このレッスンの内容を使って実際の問題を解きましょう。各チャレンジはブラウザで動き、すぐにテストされ、AI のレビューも受けられます。

すべてのチャレンジ