none
C++17削除 std::binary_function使用のテンプレートクラスへのstd::bindの引数対応について RRS feed

  • 質問

  • いつもお世話になっております。

    VS2015U3 / Windows Kits(10.0.14393.0) → VS2019 v16.9.5 / Windows Kits最新(10.0.19041.0) で作業しています。


    現在VS2019への移行をしているのですが、C++17で削除されたstd::binary_functionへの対応に絡んで、std::bindの引数について苦慮しております。

    以下ソースとエラーなのですが、テンプレートクラスにおいてのこのような場合の方法はどのようにすればよいでしょうか?

    お忙しいところ恐縮ですが、ご教示の程宜しくお願いいたします。

    template <class T1, class T2> class EqListKey //: public std::binary_function<T1, T2, bool> // C++17 binary_function 対応 { typedef T1 first_argument_type; // C++17 binary_function 対応 typedef T2 second_argument_type; // C++17 binary_function 対応 typedef bool result_type; // C++17 binary_function 対応 public: bool operator()( T1 a, T2 b ) const { return (a.iro == b.iro && a.no == b.no); } }; struct ElmTmp { int no; int iro; }; std::list<ElmTmp> ElmList; std::list<ElmTmp> ElmListTmp; ... ElmListTmp = ElmList; ... std::list<ElmTmp>::iterator it0begin = ElmList.begin(); std::list<ElmTmp>::iterator it0end = ElmList.end(); for (std::list<ElmTmp>::iterator it = ElmListTmp.begin(), end = ElmListTmp.end(); it != end; ++it) { int n = (int)std::count_if( it0begin, it0end, std::bind( EqListKey<ElmTmp, ElmTmp>(), it._Ptr->_Myval ) ); //<-----ここでのtemplateに関することだと思うのですが、 ElmListN->push_back( n ); } error C2672: 'operator __surrogate_func': 一致するオーバーロードされた関数が見つかりませんでした。 コンパイル対象の関数 テンプレート インスタンス化 'int std::count_if<std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,std::_Binder<std::_Unforced,EqListKey<ElmTmp,ElmTmp>,_Value_type &>>(_InIt,_InIt,_Pr)' のリファレンスを確認してください

    error C2893: 関数テンプレート 'unknown-type std::_Binder<std::_Unforced,EqListKey<ElmTmp,ElmTmp>,_Value_type &>::operator ()(_Unbound &&...) noexcept(<expr>) const' の特定に失敗しました



    • 編集済み SHIN109 2021年5月14日 0:06 エラーメッセージ追加
    2021年5月13日 23:49

回答

  • すでにjzkeyさんがラムダ式を提案されていますが、それが妥当に思います。

    C++11ラムダ式C++11 autoを使えば

    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (std::list<ElmTmp>::iterator it = ElmListTmp.begin(), end = ElmListTmp.end(); it != end; ++it) {
    	int n = (int)std::count_if(it0begin, it0end, [it](auto const& item) { return item.iro == it->iro && item.no == it->no; });
    	ElmListN->push_back(n);
    }

    ついでにC++11範囲for文を使えば

    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count_if(it0begin, it0end, [&tmp](auto const& item) { return item.iro == tmp.iro && item.no == tmp.no; });
    	ElmListN->push_back(n);
    }

    そもそもoperator==()を定義した方がいいかと。ついでにC++11 constexprを使って

    constexpr bool operator==(ElmTmp const& l, ElmTmp const& r) { return l.iro == r.iro && l.no == r.no; }
    
    
    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count(it0begin, it0end, tmp);
    	ElmListN->push_back(n);
    }
    

    C++20一貫比較が導入されたので、全てのメンバーで比較する場合はデフォルト宣言で十分です。

    struct  ElmTmp {
    	int no;
    	int iro;
    	bool operator==(ElmTmp const&) const = default;
    };
    
    
    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count(it0begin, it0end, tmp);
    	ElmListN->push_back(n);
    }
    

    最後にVS2019 v16.9であればC++20 std::rangesが用意されているので

    struct  ElmTmp {
    	int no;
    	int iro;
    	bool operator==(ElmTmp const&) const = default;
    };
    
    
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::ranges::count(ElmList, tmp);
    	ElmListN->push_back(n);
    }
    

    という辺りまで、おまじないをそぎ落とし、本質的なコードに専念できます。

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 11:18
  • 全部ラムダでいけるのでは?bind不要でしょ?
    [=it](const ElemTemp &v){ return *it == v;}みたいたので。
    [=eq,it](const ElemTemp &v){ return eq(*it,v);}とかが、比較オブジェクト版

    jzkey

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 2:37
  • std::bindを使うのであればこうかな

    std::bind(EqListKey<ElmTmp, ElmTmp>(), std::placeholders::_1, *it)

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 7:43

すべての返信

  • クラスにする必要ないのかな?と思い、まずこうやってみまして、通りました。

    template <class T1, class T2>
    auto EqListKey( T1 a, T2 b )
    {
    	return	(a.iro == b.iro && a.no == b.no);
    };
    
    ...
    
    	int n = (int)std::count_if(it0begin, it0end, std::bind( []( auto... args ) { return EqListKey( args... ); }, it._Ptr->_Myval, std::placeholders::_1 ));
    
    ...
    

    しかしクラスを保持していたい場合、どうするのか...。

    2021年5月14日 2:03
  • 全部ラムダでいけるのでは?bind不要でしょ?
    [=it](const ElemTemp &v){ return *it == v;}みたいたので。
    [=eq,it](const ElemTemp &v){ return eq(*it,v);}とかが、比較オブジェクト版

    jzkey

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 2:37
  • jzkey 様

    いつもお世話になっております。
    レスありがとうございます。


    なるほどです。そうですね。
    こういう感じでしょうか?

    bool eq( ElemTemp a, ElemTemp b ){
        return    (a.iro == b.iro && a.no == b.no);
    }

    このクラスに、今は関数一つですが、他の関数も持っておきたいなどがある場合は、どうなるのかなと気になっております。
    2021年5月14日 5:31
  • std::bindを使うのであればこうかな

    std::bind(EqListKey<ElmTmp, ElmTmp>(), std::placeholders::_1, *it)

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 7:43
  • すでにjzkeyさんがラムダ式を提案されていますが、それが妥当に思います。

    C++11ラムダ式C++11 autoを使えば

    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (std::list<ElmTmp>::iterator it = ElmListTmp.begin(), end = ElmListTmp.end(); it != end; ++it) {
    	int n = (int)std::count_if(it0begin, it0end, [it](auto const& item) { return item.iro == it->iro && item.no == it->no; });
    	ElmListN->push_back(n);
    }

    ついでにC++11範囲for文を使えば

    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count_if(it0begin, it0end, [&tmp](auto const& item) { return item.iro == tmp.iro && item.no == tmp.no; });
    	ElmListN->push_back(n);
    }

    そもそもoperator==()を定義した方がいいかと。ついでにC++11 constexprを使って

    constexpr bool operator==(ElmTmp const& l, ElmTmp const& r) { return l.iro == r.iro && l.no == r.no; }
    
    
    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count(it0begin, it0end, tmp);
    	ElmListN->push_back(n);
    }
    

    C++20一貫比較が導入されたので、全てのメンバーで比較する場合はデフォルト宣言で十分です。

    struct  ElmTmp {
    	int no;
    	int iro;
    	bool operator==(ElmTmp const&) const = default;
    };
    
    
    std::list<ElmTmp>::iterator it0begin = ElmList.begin();
    std::list<ElmTmp>::iterator it0end = ElmList.end();
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::count(it0begin, it0end, tmp);
    	ElmListN->push_back(n);
    }
    

    最後にVS2019 v16.9であればC++20 std::rangesが用意されているので

    struct  ElmTmp {
    	int no;
    	int iro;
    	bool operator==(ElmTmp const&) const = default;
    };
    
    
    for (auto const& tmp : ElmListTmp) {
    	int n = (int)std::ranges::count(ElmList, tmp);
    	ElmListN->push_back(n);
    }
    

    という辺りまで、おまじないをそぎ落とし、本質的なコードに専念できます。

    • 回答としてマーク SHIN109 2021年5月17日 0:51
    2021年5月14日 11:18
  • Hongliang 様、佐裕理 様

    いつもお世話になっております。ありがとうございます。
    レスありがとうございました。


    Hongliang 様

    それもやってみていたのですが、私の引数順、逆でした。ありがとうございます。


    佐裕理 様

    どんどんシンプルでより良くなっているのですね。
    また良かったものも時代で非推奨になり、やがて削除になっていくので、あっという間に取り残されてしまいます。
    アンテナを強力にしていないといけないですね。




    皆さま、ご丁寧にご教示ありがとうございました。
    2021年5月17日 0:51