Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::functionのメリット/デメリットとは #53

Open
yumetodo opened this issue Nov 23, 2020 · 4 comments
Open

std::functionのメリット/デメリットとは #53

yumetodo opened this issue Nov 23, 2020 · 4 comments

Comments

@yumetodo
Copy link
Contributor

https://rinatz.github.io/cpp-book/ch02-09-function-objects/

std::function で関数ポインタやラムダ式を保持することが出来ます。 std::function を使うことで、関数ポインタのような複雑な構文が簡潔になるという利点もあります。

という説明では、「じゃあ全部std::functionを使えばいいのか!」というミスリードをしかねないという印象を受けます。最適化を阻害するというデメリットを語らないのはフェアではないように感じます。

また、std::functionのメリットが簡潔に書けるだけというのも違う気がします。それだけならusing aliasで型定義すれば済むはず。そうではなくて、型消去によって関数オブジェクトを配列に格納できるということが利点であるはずです。lambda式を含む関数オブジェクトは、引数と戻り値の型が一致していても別の型をもつため、配列に格納できないところを、型消去してくれるstd::functionによって達成できるわけですよね。

@rinatz
Copy link
Owner

rinatz commented Nov 23, 2020

最適化の話はご指摘の通りだとして

関数オブジェクトを配列に格納できる

std::function のメリットって配列に格納できることだけじゃないと思ってますが、合ってますでしょうか。
コールバックやクラスのメンバ変数としても定義できるのはメリットの内には入らないということでしょうか?

@SaitoAtsushi
Copy link

動的に型をディスパッチできることがメリットであり、そのために実行時コストが生じることがデメリットでしょう。

配列の要素、コールバック、データメンバのいずれにしてもコンパイル時に型が確定するなら std::function は不適当ですし、実行時の選択が必要なら std::function は有用です。

@yumetodo
Copy link
Contributor Author

yumetodo commented Nov 24, 2020

コンパイル時に確定させられるなら、templateを使えばいいだけの話ですから、その場合std::functionはいらないですね。

#include <type_traits>
template<typename Func>
class Foo {
    Func f;
public:
    template<typename F, std::enable_if_t<std::is_invocable_r_v<int, Func, int>, std::nullptr_t> = nullptr>
    Foo(F&& f) : f(std::forward<F>(f))
    {}
};
template<typename F, std::nullptr_t = nullptr>
Foo(F&&) -> Foo<std::remove_cv_t<std::remove_reference_t<F>>>;

@yohhoy
Copy link

yohhoy commented Nov 26, 2020

SaitoAtsushi さんコメントに関連した追加情報です。

動的に型をディスパッチできることがメリットであり、そのために実行時コストが生じることがデメリットでしょう。

C++標準ではstd::functionを「1: 関数ポインタ表記を一般化した多態的(polymorphic)なラッパー」「2: 関数を 第一級(first-class)オブジェクト として扱える」と説明しています。[func.wrap]/1

The function class template provides polymorphic wrappers that generalize the notion of a function pointer. Wrappers can store, copy, and call arbitrary callable objects ([func.def]), given a call signature ([func.def]), allowing functions to be first-class objects.

より噛み砕いた表現に言い換えるならば、下記のようになるでしょうか:

  • 1: 普通の関数から関数オブジェクトやラムダ式まで、「関数のように呼び出せるモノ」を統一的なインタフェースで取り扱うクラステンプレートである。
  • 2: 前述の「関数のように呼び出せるモノ」を、代入したりコピーしたりと普通の変数型のように扱えるクラステンプレートである。

配列に格納できる
コールバックやクラスのメンバ変数としても定義できる

rinatz さんが挙げたメリットは「2: 第一級オブジェクトとして扱える」ことの恩恵と言えます。


一方のデメリットはC++標準には言及ありませんが、挙げるととしたら下記が考えられます:

  • 実行時に動的メモリ確保/解放処理が必要とされるため、それによる時間的・空間的オーバーヘッドが生じる。
  • 内部実装に型消去技法が使われるためコンパイル時最適化(インライン展開)が阻害され、テンプレートに直接関数オブジェクトを渡すケースに比べて時間的オーバヘッドが生じる。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants