1. ホーム
  2. c++

[解決済み] C++でstd::functionと関数ポインタのどちらを使うべきですか?

2022-04-23 16:57:40

質問

C++でコールバック関数を実装する場合、Cスタイルの関数ポインタを使用した方が良いですか?

void (*callbackFunc)(int);

それともstd::functionを利用した方がいいのでしょうか。

std::function< void(int) > callbackFunc;

解決方法は?

要するに std::function 特に理由がない限り

関数ポインタは、以下のような欠点があります。 をキャプチャすることができません。 あるコンテクストがあります。例えば、ラムダ関数をコールバックとして渡して、コンテキスト変数を取得することはできません(ただし、何も取得しない場合は動作します)。オブジェクトのメンバ変数 (つまり非静的変数) を呼び出すことも、オブジェクト ( this -ポインタ)をキャプチャする必要があります。 (1)

std::function (C++11以降)は、主に ストア を関数として使用することができます(渡すだけでは格納する必要はありません)。したがって、例えばコールバックをメンバ変数に格納したい場合は、おそらくこれが最良の選択となる。しかし、コールバックを保存しない場合、これは良い最初の選択肢となります。これは非常に普遍的なものです。一貫性のある読みやすいコードにこだわりがあり、すべての選択について考えたくない(つまり、シンプルでありたい)場合は std::function を渡すすべての関数に使用します。

3つ目の選択肢を考えてみましょう。もし、小さな関数を実装して、その関数が提供するコールバック関数を通じて何かを報告するのであれば、その関数の中に テンプレートパラメータ にすることができます。 任意の呼び出し可能なオブジェクト すなわち、関数ポインタ、ファンクタ、ラムダ、 std::function , ... この欠点は、(外側の)関数がテンプレートになるため、ヘッダで実装する必要があることです。一方、コールバックの呼び出しはインライン化できるという利点があります。なぜなら、外側の関数のクライアントコードは、コールバックの呼び出しを正確に理解し、型情報を利用することができるからです。

テンプレート・パラメータを使用したバージョンの例(write & の代わりに && はC++11以前のバージョン)。

template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
    ...
    callback(...);
    ...
}


以下の表でお分かりのように、どれも一長一短がありますね。

<テーブル 関数 ptr std::関数 テンプレートパラメータ コンテキスト変数をキャプチャすることができます いいえ 1 はい はい コールオーバーヘッドなし(コメント参照) あり いいえ はい はインライン化可能です(コメント参照) いいえ いいえ はい は、クラスメンバに格納することができます。 あり はい いいえ 2 ヘッダーの外側に実装可能 あり はい いいえ C++11標準なしでサポートされている あり いいえ 3 はい 読みやすい(個人の感想です) いいえ はい (はい)

<サブ (1) この制限を克服するための回避策があります。例えば、追加データを(外側の)関数のさらなるパラメータとして渡すことができます。 myFunction(..., callback, data) が呼び出されます。 callback(data) . これは、C++でも可能ですが(ちなみにWIN32 APIで多用されています)、C++ではより良いオプションがあるので避けるべきです。

<サブ (2) クラステンプレート、つまり関数を格納するクラスがテンプレートであるという話でなければ。しかし、それではクライアント側で関数の型がコールバックを格納するオブジェクトの型を決定することになり、実際のユースケースではほとんど選択肢に入りません。

<サブ (3) C++11以前のバージョンでは boost::function