サーチ…


簡単な使い方

#include <iostream>
#include <functional>
std::function<void(int , const std::string&)> myFuncObj;
void theFunc(int i, const std::string& s)
{
    std::cout << s << ": " << i << std::endl;
}
int main(int argc, char *argv[])
{
    myFuncObj = theFunc;
    myFuncObj(10, "hello world");
}

std :: bindで使用されるstd :: function

引数を持つ関数をコールバックする必要がある状況を考えてみましょう。 std::function std::bind使用されるstd::functionは、以下に示すような非常に強力な設計構造を提供します。

class A
{
public:
    std::function<void(int, const std::string&)> m_CbFunc = nullptr;
    void foo()
    {
        if (m_CbFunc)
        {
            m_CbFunc(100, "event fired");
        }
    }

};

class B
{
public:
    B()
    {
        auto aFunc = std::bind(&B::eventHandler, this, std::placeholders::_1, std::placeholders::_2);
        anObjA.m_CbFunc = aFunc;
    }
    void eventHandler(int i, const std::string& s)
    {
        std::cout << s << ": " << i << std::endl;
    }

    void DoSomethingOnA()
    {
        anObjA.foo();
    }

    A anObjA;
};

int main(int argc, char *argv[])
{
     B anObjB;
     anObjB.DoSomethingOnA();
}

std :: functionをlambdaとstd :: bindで実行する

#include <iostream>
#include <functional>

using std::placeholders::_1; // to be used in std::bind example

int stdf_foobar (int x, std::function<int(int)> moo)
{
    return x + moo(x); // std::function moo called
}

int foo (int x) { return 2+x; }

int foo_2 (int x, int y) { return 9*x + y; }

int main()
{
    int a = 2;

    /* Function pointers */
    std::cout << stdf_foobar(a, &foo) << std::endl; // 6 ( 2 + (2+2) )
    // can also be: stdf_foobar(2, foo)

    /* Lambda expressions */
    /* An unnamed closure from a lambda expression can be
     * stored in a std::function object:
     */
    int capture_value = 3;
    std::cout << stdf_foobar(a,
                             [capture_value](int param) -> int { return 7 + capture_value * param; })
              << std::endl;
    // result: 15 ==  value + (7 * capture_value * value) == 2 + (7 + 3 * 2)

    /* std::bind expressions */
    /* The result of a std::bind expression can be passed.
     * For example by binding parameters to a function pointer call:
     */    
    int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
    std::cout << b << std::endl;
    // b == 23 == 2 + ( 9*2 + 3 )
    int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
    std::cout << c << std::endl;
    // c == 49 == 2 + ( 9*5 + 2 )

    return 0;
}

`関数`オーバーヘッド

std::functionは重大なオーバーヘッドを引き起こす可能性があります。 std::functionは[value semantics] [1]があるため、指定された呼び出し可能コードをコピーまたは移動する必要があります。しかし、それは任意のタイプの呼び出し可能なものを取ることができるので、頻繁にこれを行うためにメモリを動的に割り当てる必要があります。

function実装の中には、小さなオブジェクト型(関数ポインタ、メンバポインタ、非常に小さな状態のファンクタなど)がfunctionオブジェクトに直接格納される、いわゆる「小オブジェクト最適化」があります。しかし、これは、型がnoexcept move constructibleである場合にのみ機能します。さらに、C ++標準では、すべての実装で1つを提供する必要はありません。

次の点を考慮してください。

//Header file
using MyPredicate = std::function<bool(const MyValue &, const MyValue &)>;

void SortMyContainer(MyContainer &C, const MyPredicate &pred);

//Source file
void SortMyContainer(MyContainer &C, const MyPredicate &pred)
{
    std::sort(C.begin(), C.end(), pred);
}

テンプレートパラメータはSortMyContainerの推奨ソリューションですが、何らかの理由でこれが可能ではない、または望ましいとは考えません。 SortMyContainerは、自身の呼び出しを超えてpredを格納する必要はありません。しかし、与えられたファンクタが単純な大きさであれば、 predはメモリを十分に割り当てることができます。

functionは何かをコピー/移動する必要があるためメモリを割り当てます。 functionは与えられた呼び出し可能コードの所有権を取得します。しかし、 SortMyContainerは呼び出し可能なものを所有する必要はありません。それはそれを参照しているだけです。ここでのfunction使用は過剰です。効率的かもしれませんが、そうでないかもしれません。

単に呼び出し可能コードを参照する標準ライブラリ関数型はありません。だから、代わりの解決策が見つかるか、オーバーヘッドで生活することを選択する必要があります。

また、 functionは、オブジェクトのメモリ割り当てがどこから来るかを制御する有効な手段はありません。はい、それは取るコンストラクタがあるallocator 、しかし[多くの実装は、それらを正しく実装する...か、まったくありません] [2]。

C ++ 17

allocatorを受け取るfunctionコンストラクタはもはやその型の一部ではありません。したがって、割り当てを管理する方法はありません。

function呼び出すことは、内容を直接呼び出すよりも遅くなります。どののでfunctionインスタンスが呼び出し可能に保持することができ、介してコールfunction 、間接的でなければなりません。 function呼び出しのオーバーヘッドは、仮想関数呼び出しのオーダーです。

他の呼び出し可能な型へのstd :: functionのバインド

/*
 * This example show some ways of using std::function to call
 *  a) C-like function
 *  b) class-member function
 *  c) operator()
 *  d) lambda function
 *
 * Function call can be made:
 *  a) with right arguments
 *  b) argumens with different order, types and count
 */
#include <iostream>
#include <functional>
#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using namespace std::placeholders;



// simple function to be called
double foo_fn(int x, float y, double z)
{
  double res = x + y + z;
  std::cout << "foo_fn called with arguments: " 
            << x << ", " << y << ", " << z 
            << " result is : " << res 
            << std::endl;
  return res;
}

// structure with member function to call
struct foo_struct
{
    // member function to call
    double foo_fn(int x, float y, double z)
    {
        double res = x + y + z;
        std::cout << "foo_struct::foo_fn called with arguments: " 
                << x << ", " << y << ", " << z 
                << " result is : " << res 
                << std::endl;
        return res;
    }
    // this member function has different signature - but it can be used too
    // please not that argument order is changed too
    double foo_fn_4(int x, double z, float y, long xx)
    {
        double res = x + y + z + xx;
        std::cout << "foo_struct::foo_fn_4 called with arguments: " 
                << x << ", " << z << ", " << y << ", " << xx
                << " result is : " << res 
                << std::endl;
        return res;
    }
    // overloaded operator() makes whole object to be callable
    double operator()(int x, float y, double z)
    {
        double res = x + y + z;
        std::cout << "foo_struct::operator() called with arguments: " 
                << x << ", " << y << ", " << z 
                << " result is : " << res 
                << std::endl;
        return res;
    }
};


int main(void)
{
  // typedefs
  using function_type = std::function<double(int, float, double)>;

  // foo_struct instance
  foo_struct fs;
  
  // here we will store all binded functions 
  std::vector<function_type> bindings;

  // var #1 - you can use simple function
  function_type var1 = foo_fn;
  bindings.push_back(var1);
  
  // var #2 - you can use member function 
  function_type var2 = std::bind(&foo_struct::foo_fn, fs, _1, _2, _3);
  bindings.push_back(var2);
  
  // var #3 - you can use member function with different signature
  // foo_fn_4 has different count of arguments and types
  function_type var3 = std::bind(&foo_struct::foo_fn_4, fs, _1, _3, _2, 0l);
  bindings.push_back(var3);

  // var #4 - you can use object with overloaded operator() 
  function_type var4 = fs;
  bindings.push_back(var4);

  // var #5 - you can use lambda function
  function_type var5 = [](int x, float y, double z)
    {
        double res = x + y + z;
        std::cout << "lambda  called with arguments: " 
                << x << ", " << y << ", " << z 
                << " result is : " << res 
                << std::endl;
        return res;
    };
  bindings.push_back(var5);
    
  std::cout << "Test stored functions with arguments: x = 1, y = 2, z = 3" 
            << std::endl;
  
  for (auto f : bindings)
      f(1, 2, 3);
      
}

ライブ

出力:

Test stored functions with arguments: x = 1, y = 2, z = 3
foo_fn called with arguments: 1, 2, 3 result is : 6
foo_struct::foo_fn called with arguments: 1, 2, 3 result is : 6
foo_struct::foo_fn_4 called with arguments: 1, 3, 2, 0 result is : 6
foo_struct::operator() called with arguments: 1, 2, 3 result is : 6
lambda  called with arguments: 1, 2, 3 result is : 6

関数引数をstd :: tupleに格納する

いくつかのプログラムでは、将来のある種の関数呼び出しのために引数を格納する必要があります。

次の例は、std :: tupleに格納された引数を持つ関数を呼び出す方法を示しています。

#include <iostream>
#include <functional>
#include <tuple>
#include <iostream>

// simple function to be called
double foo_fn(int x, float y, double z)
{
   double res =  x + y + z;
   std::cout << "foo_fn called. x = " << x << " y = " << y << " z = " << z
             << " res=" << res;
   return res;
}

// helpers for tuple unrolling
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

// invocation helper 
template<typename FN, typename P, int ...S>
double call_fn_internal(const FN& fn, const P& params, const seq<S...>)
{
   return fn(std::get<S>(params) ...);
}
// call function with arguments stored in std::tuple
template<typename Ret, typename ...Args>
Ret call_fn(const std::function<Ret(Args...)>& fn, 
            const std::tuple<Args...>& params)
{
    return call_fn_internal(fn, params, typename gens<sizeof...(Args)>::type());
}


int main(void)
{
  // arguments
  std::tuple<int, float, double> t = std::make_tuple(1, 5, 10);
  // function to call
  std::function<double(int, float, double)> fn = foo_fn;
  
  // invoke a function with stored arguments
  call_fn(fn, t);
}

ライブ

出力:

foo_fn called. x = 1 y = 5 z = 10 res=16


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow