読者です 読者をやめる 読者になる 読者になる

ぷろみん

プログラミング的な内容を扱ってます

関数ポインタよりもstd::functionを使おう

アンドロイドアプリ作りました

play.google.com 良かったらプレイしてみてください。

書きやすさ

// 関数ポインタ
void (*Func)(void);
void (Foo::*Func2)(void);

// std::function
std::function<void(void)> Func;
std::function<void(Foo*)> Func2;

これは慣れてないと両方読みづらいかもしれませんね。
慣れるとfunctionの方が型名、変数名といういつものルールに従っているので分かりやすくなります。

運搬性

void Test(){}
// 関数ポインタ
using ReturnFunction = void (*)();
ReturnFunction Func(){
    return Test;
}
// C++14から以下の書き方もできる
auto Func(){
    return Test;
}

// std::function
std::function<void()> Func(){
    return Test;
}

戻り値に関数ポインタを使う場合にはusingかtypedef宣言が必要になります。
autoを使えば省略できますが以下の事に注意する必要があります。

auto Func(){
    return Test;
}

void Func2(ReturnFunction a){}
void Func2(std::function<void()> a){}

int main(){
    auto a = Func();
    // 関数ポインタ版の方が優先して呼ばれる
    Func2(a);
}

部分適用

これは関数ポインタでは実現できないと思います。

void Int3(int, int, int){}

int main(){
    std::function<void(int, int, int)> a = Int3;
    // 3番目の引数を3で固定した引数が2個の関数を生成する
    std::function<void(int, int)> b = std::bind(a, std::placeholders::_1, std::placeholders::_2, 3);
}

この機能はメンバ関数ポインタと合わせると更に強力で、メンバ関数インスタンスを拘束して通常の関数の様に扱ったりできます。

class Foo{
public:
    void FooFunc(){}
};

int main(){
    Foo foo;
    std::function<void()> Func = std::bind(&Foo::FooFunc, &foo);
    // メンバ関数を通常の関数として呼べる
    Func();
}

初期値

// 関数ポインタ
ReturnFunction a = nullptr;
if(a != nullptr){
    a();
}

// std::function
// 明示的初期化必要無し
std::function<void()> a;
// operator boolが設定してあるのでチェックも容易
if(a){
    a();
}

ラムダ

std::functionならラムダの運搬も可能です。

std::function<void(void)> a = []{};

ただし、キャプチャにローカルオブジェクトを含む場合不定値になるので注意が必要です。