1. 程式人生 > >STL基礎--仿函數(函數對象)

STL基礎--仿函數(函數對象)

log 需要 emp and 謂詞 code pow 條件 bind2nd

1 首先看個仿函數的例子

class X {
   public:
   void operator()(string str) {     // 函數調用運算符,返回類型在operator之前
      cout << "Calling functor X with parameter " << str<< endl;
   }  
    operator string () const { return "X"; } //類型轉換函數,返回類型在operator之後
};

int main()
{
   X foo;
   foo("Hi");    // 以參數HI調用仿函數X
}
/*
 * 仿函數的好處:
 * 1. 智能函數: 比常規函數有更多的功能,比如可以保存狀態(參數化的函數)
 * 2. 有它自己的類型,不用考慮函數重名問題
 * 事實上,類帶來的其他好處,封裝,繼承,多態都可以是仿函數的好處
 */

2 參數化的函數

class X {
   public:
   X(int i) {}
   void operator()(string str) { 
      cout << "Calling functor X with parameter " << str<< endl;
   }
};

int main()
{
   X(8)("Hi");
}

// 為什麽需要參數化函數,用2個參數的函數代替不行麽?


// 只實現+2的功能,如果我想實現加任何數的功能
void add2(int i) {
   cout << i+2 << endl;
}

// 用全局變量,顯然不好
// 用模板實現,加任何值的功能,但是val需要編譯時常數
template<int val>
void addVal(int i) {
   cout << val+i << endl;
}

// 輪到仿函數登場了
class AddValue {
   int val;
   public:
   AddValue(int j) : val(j) { }
   void operator()(int i) {
      cout << i+val << endl;
   }
};

int main()
{
   vector<int> vec = { 2, 3, 4, 5};   
   //for_each(vec.begin(), vec.end(), add2); // {4, 5, 6, 7}
   int x = 2;
   //for_each(vec.begin(), vec.end(), addVal<x>); // {4, 5, 6, 7} ,編譯不過
   for_each(vec.begin(), vec.end(), AddValue(x)); // {4, 5, 6, 7}
}

3 內置的仿函數

// 比較 less greater  greater_equal  less_equal  not_equal_to
// 邏輯 logical_and  logical_not  logical_or
// 算術 multiplies minus  plus  divide  modulus  negate

int x = multiplies<int>()(3,4);  //  x = 3 * 4 

if (not_equal_to<int>()(x, 10))   // if (x != 10)
   cout << x << endl;

4 參數綁定

set<int> myset = { 2, 3, 4, 5};   
vector<int> vec;


int x = multiplies<int>()(3,4);  //  x = 3 * 4 

// 將元素值乘以10,保存在vec中:
transform(myset.begin(), myset.end(),    // 源
          back_inserter(vec),              // 目的
            bind(multiplies<int>(), placeholders::_1, 10));  // 仿函數
    // multiplies<int>()的第1個參數替換為myset中的元素值
    // vec: {20, 30, 40, 50}


void addVal(int i, int val) {
   cout << i+val << endl;
}
for_each(vec.begin(), vec.end(), bind(addVal, placeholders::_1, 2));

// C++ 03: bind1st, bind2nd

5 將常規函數轉為仿函數

double Pow(double x, double y) {
    return pow(x, y);
}

int main()
{
  set<int> myset = {3, 1, 25, 7, 12};
  deque<int> d;
  auto f = function<double (double,double)>(Pow);   //C++ 11
  transform(myset.begin(), myset.end(),     // 源地址
              back_inserter(d),              // 目的
                bind(f, placeholders::_1, 2));  // 仿函數
            //  d: {1, 9, 49, 144, 625}
}
// C++ 03中使用ptr_fun 

6 lambda函數

// 想拷貝5<x<20之間的元素到d
set<int> myset = {3, 1, 25, 7, 12};
deque<int> d;

// 定義一個函數
bool needCopy(int x){
   return (x>20)||(x<5);
}
// 或者用內置仿函數和bind結合
// 兩種方法都不方便

transform(myset.begin(), myset.end(),     // source
          back_inserter(d),               // destination
          needCopy
          );

/*
          bind(logical_or<bool>, 
              bind(greater<int>(), placeholders::_1, 20),
              bind(less<int>(), placeholders::_1, 5))
*/

// C++ 11 lambda function:
transform(myset.begin(), myset.end(),     // source
          back_inserter(d),              // destination
          [](int x){return (x>20)||(x<5);}    //lambda函數
          );

7 為什麽STL中需要仿函數?

set<int> myset = {3, 1, 25, 7, 12}; // myset: {1, 3, 7, 12, 25}
// 同:
set<int, less<int> > myset = {3, 1, 25, 7, 12};

bool lsb_less(int x, int y) {
      return (x%10)<(y%10);
}

class Lsb_less {
   public:
   bool operator()(int x, int y) {
      return (x%10)<(y%10);
   }
};
int main()
{
  set<int, Lsb_less> myset = {3, 1, 25, 7, 12};  // myset: {1,12,3,25,7}
  ...
}

8 謂詞

 /*
 * 一種特殊的仿函數:
 * 1. 返回一個boolean值
 * 2. 不改變數據
 */

class NeedCopy {
   bool operator()(int x){   
      return (x>20)||(x<5);  
   }
};

transform(myset.begin(), myset.end(),     // source
          back_inserter(d),               // destination
          NeedCopy()
          );

// 謂詞用於比較或者條件判斷

STL基礎--仿函數(函數對象)