Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

std::function, std::bind, std::mem_fnでハマって解決したのでメモ

スポンサードリンク

f:id:fa11enprince:20200628231359j:plain
std::functionにメンバ関数を入れてやるにはどうすればいいのやらと試行錯誤。
まずは非メンバ関数の場合
こちらは悩まずこんな感じでいけた。

std::function 非メンバ関数版

#include <iostream>
#include <unordered_map>
#include <functional>

template <typename T>
T plus(T p1, T p2)
{
    return p1 + p2;
}
template <typename T>
T minus(T p1, T p2)
{
    return p1 - p2;
}
template <typename T>
T multi(T p1, T p2)
{
    return p1 * p2;
}
template <typename T>
T div(T p1, T p2)
{
    return p1 / p2;
}

template <typename T>
class Func
{
private:
    T operatnd1_;   
    T operatnd2_;   

    std::unordered_map<
        std::string,
        std::function<T (T, T)>
    > optr_;

public:
    Func()
    {
        // とりあえず下記だけサポート
        optr_["+"] = plus<T>;
        optr_["-"] = minus<T>;
        optr_["*"] = multi<T>;
        optr_["/"] = div<T>;
    };
    T call(std::string ope, T p1, T p2)
    {
        // 例外処理しない(手抜き)
        return optr_[ope](p1, p2); 
    }
};

int main()
{
    Func<double> f;
    std::cout << f.call("+", 1, 2) << '\n';
    std::cout << f.call("-", 1, 2) << '\n';
    std::cout << f.call("*", 1, 2) << '\n';
    std::cout << f.call("/", 1, 2) << '\n';
    return 0;
}

一応出力結果

3
-1
2
0.5

std::function メンバ関数版

#include <iostream>
#include <unordered_map>
#include <functional>

template <typename T>
class Func
{
private:
    T operatnd1_;   
    T operatnd2_;   

    std::unordered_map<
        std::string,
        //std::function<T (Func<T>::*)(T, T)> // こうじゃなくて単純にT (T, T)
        std::function<T (T, T)>
    > optr_;

    T plus(T p1, T p2)
    {
        return p1 + p2;
    }
    T minus(T p1, T p2)
    {
        return p1 - p2;
    }
    T multi(T p1, T p2)
    {
        return p1 * p2;
    }
    T div(T p1, T p2)
    {
        return p1 / p2;
    }

public:
    Func()
    {
        // mem_fnじゃなくてbindを使うっぽい
        using namespace std::placeholders;
        // とりあえず下記だけサポート
        // 自クラス内のメソッド指定の場合はthisを指定
        // 引数が複数ある場合はplaceholdersで指定してやる
        optr_["+"] = std::bind(&Func<T>::plus, this, _1, _2);
        optr_["-"] = std::bind(&Func<T>::minus, this, _1, _2);
        optr_["*"] = std::bind(&Func<T>::multi, this, _1, _2);
        optr_["/"] = std::bind(&Func<T>::div, this, _1, _2);
    };
    T call(std::string ope, T p1, T p2)
    {
        // 例外処理しない(手抜き)
        return optr_[ope](p1, p2); 
    }
};

int main()
{
    Func<double> f;
    std::cout << f.call("+", 1, 2) << '\n';
    std::cout << f.call("-", 1, 2) << '\n';
    std::cout << f.call("*", 1, 2) << '\n';
    std::cout << f.call("/", 1, 2) << '\n';
    return 0;
}

同じだけど一応出力結果

3
-1
2
0.5

bind使って_1,_2を指定してやればいいのかとわかり解決。
ラムダを使いたいところですが、テンプレートの場合はラムダダメだった気がする……。
メンバ関数ポインタ版で書くと…カオスになるからやめよ(´・ω・`)
と思ったけど書いてみた。

メンバ関数ポインタ版

#include <iostream>
#include <unordered_map>

template <typename T>
class Func
{
private:
    T operatnd1_;   
    T operatnd2_;   

    // メンバ関数ポインタにPFUNCという名前を付ける
    typedef T (Func<T>::*PFUNC)(T, T); 

    std::unordered_map<
        std::string,
        PFUNC
    > optr_;

    T plus(T p1, T p2)
    {
        return p1 + p2;
    }
    T minus(T p1, T p2)
    {
        return p1 - p2;
    }
    T multi(T p1, T p2)
    {
        return p1 * p2;
    }
    T div(T p1, T p2)
    {
        return p1 / p2;
    }

public:
    Func()
    {
        optr_["+"] = &Func<T>::plus;
        optr_["-"] = &Func<T>::minus;
        optr_["*"] = &Func<T>::multi;
        optr_["/"] = &Func<T>::div;
    };
    T call(std::string ope, T p1, T p2)
    {
        return (this->*optr_[ope])(p1, p2); 
    }
};

int main()
{
    Func<double> f;
    std::cout << f.call("+", 1, 2) << '\n';
    std::cout << f.call("-", 1, 2) << '\n';
    std::cout << f.call("*", 1, 2) << '\n';
    std::cout << f.call("/", 1, 2) << '\n';
    return 0;
}

どうにもFunc::*PFUNCやら->*やらがうーん(´・ω・`)

参考