C++之仿函数_c++仿函数-程序员宅基地

技术标签: C++  

最近再看STL源码的时候看到里面的实现用了大量的仿函数,然后上网搜集了一些关于仿函数的知识。

仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。

如果编程者要将某种“操作”当做算法的参数,一般有两种方法:
(1)一个办法就是先将该“操作”设计为一个函数,再将函数指针当做算法的一个参数。比如说我们定义一个排序函数,比较函数操作可以设计一个函数,利用函数指针作为参数传递给排序函数
(2)将该“操作”设计为一个仿函数(就语言层面而言是个 class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数,接下来我们就详细讲一下如何设计仿函数。

如下是定义了一个比较仿函数:
 

class comp
{
public:
    explicit comp(int t):a(t){}//显式构造函数
    bool operator()(int num) const//const放前面表示这个函数的返回值是不可修改的,放后面表示这个函数不修改当前对象的成员。
    {
        return num>a;
    }
private:
    const int a;
};
int count(int *start,int *end,comp a)
{
    int *b=start;
    int c=0;
    while(b<=end)
    {
        c+=a(*b)?1:0;
        b++;
    }
    return c;
}
int main()
{
    comp a(10);
    cout<<a(11)<<endl;
    cout<<a(9)<<endl;
    int aa[4]={0,10,20,30};
    int result=count(aa,aa+3,a);
    cout<<result<<endl;
    return 0;
}

运行结果:

1
0
2
按 <RETURN> 来关闭窗口...

STL中涉及到了大量的仿函数,一般都用模板实现

1、STL中基础仿函数

1、仿函数定义自己型别
算法内部可能需要使用仿函数返回值或者输出值的类型参数,因此定义两个类。

//一元函数的参数和返回值类型,通常被继承
template <class Arg, class Result>
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};

//二元函数的参数和返回值类型,通常被继承
template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};  

上述两个类分别定义了一元和二元函数参数和返回值型别,对应的仿函数类仅仅需要继承此类即可。

2、算术类仿函数
标准库给我们定义了一些通用的仿函数,可以直接调用,生成对象,面向用户。

//算术类仿函数 + - * / %
//plus仿函数,生成一个对象,里面仅仅有一个函数重载的方法。
template <class T>
struct plus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x + y; }
};

//minus仿函数
template <class T>
struct minus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x - y; }
};

template <class T>
struct multiplies : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x * y; }
};

template <class T>
struct divides : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x / y; }
};
template <class T>
struct modulus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x % y; }
};

//取负值
template <class T>
struct negate : public unary_function<T, T> {
    T operator()(const T& x) const { return -x; }
};

单独使用仿函数,通常将仿函数和算法部分单独分开使用。

#include <iostream>     // std::cout
#include <functional>   // std::plus
#include <algorithm>    // std::transform
using namespace std;
int main(void)
{
    cout << minus<int>()(10,5) << endl;//5
    cout << multiplies<int>()(10,5) << endl;//50
    cout << divides<int>()(10,5) << endl;//2
    cout << modulus<int>()(10,5) << endl;//0
    cout << negate<int>()(10) << endl;//-10
    return 0;
}

运行结果:

5
50
2
0
-10

3、关系运算符仿函数

//关系运算符仿函数
// x==y 仿函数
template <class T>
struct equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x == y; }
};

// x!=y 仿函数
template <class T>
struct not_equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x != y; }
};
// x>y 仿函数
template <class T>
struct greater : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x > y; }
};
// x<y 仿函数
template <class T>
struct less : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x < y; }
};

// x>=y 仿函数
template <class T>
struct greater_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x >= y; }
};
// x<=y 仿函数
template <class T>
struct less_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x <= y; }
};
// sort algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
#include <functional>   // std::

bool myfunction (int i,int j) { return (i < j); }

int main () {
  int myints[] = {32,71,12,45,26,80,53,33};
  std::vector<int> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33

  // using default comparison (operator <):
  std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33

  // using function as comp
  std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)

  // using object as comp
  std::sort (myvector.begin(), myvector.end(), std::less<int>());     //(12 26 32 33 45 53 71 80)

  // print out content:
  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

 运行结果:

myvector contains:12 26 32 33 45 53 71 80

4、逻辑运算符仿函数

template <class T>
struct logical_and : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x && y; }
};

template <class T>
struct logical_or : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x || y; }
};

template <class T>
struct logical_not : public unary_function<T, bool> {
    bool operator()(const T& x) const { return !x; }
};

5、证同、选择、投射仿函数

///
//证同仿函数,主要用于RB或者hashmap里面 key = value情况
template <class T>
struct identity : public unary_function<T, T> {
  const T& operator()(const T& x) const { return x; }
};

//选择仿函数,主要用与RB和hashmap里面key 不为value情况,从pair种取出key
template <class Pair>
struct select1st : public unary_function<Pair, typename Pair::first_type> {
  const typename Pair::first_type& operator()(const Pair& x) const
  {
    return x.first;
  }
};

//选择仿函数,主要用与RB和hashmap里面key 不为value情况,从pair种取出value
template <class Pair>
struct select2nd : public unary_function<Pair, typename Pair::second_type> {
  const typename Pair::second_type& operator()(const Pair& x) const
  {
    return x.second;
  }
};

//投射函数,输入x和y返回x
template <class Arg1, class Arg2>
struct project1st : public binary_function<Arg1, Arg2, Arg1> {
  Arg1 operator()(const Arg1& x, const Arg2&) const { return x; }
};
//投射函数,输入x和y返回y
template <class Arg1, class Arg2>
struct project2nd : public binary_function<Arg1, Arg2, Arg2> {
  Arg2 operator()(const Arg1&, const Arg2& y) const { return y; }
};
/

3、STL中仿函数适配器

仿函数适配器是通过将上述仿函数重新配置成含有新功能的模板函数。

1、对仿函数返回值进行否定适配器
传入仿函数对象即可,和以前一样使用,仅仅包装了一下子而已。

//否定一元返回值
//模板参数传入仿函数类
template <class Predicate>
class unary_negate
  : public unary_function<typename Predicate::argument_type, bool> {
protected:
  Predicate pred;//对象
public:
  explicit unary_negate(const Predicate& x) : pred(x) {}
  bool operator()(const typename Predicate::argument_type& x) const {
    return !pred(x);//这里是调用的关键
  }
};
//辅助函数,使得我们方便使用unary_negate<Pred>
//传入对象,并返回临时对象。
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {
  return unary_negate<Predicate>(pred);//返回临时对象
}
//辅助函数,识别传入对象,通过模板萃取其模板型别,然后声明模板声明临时对象并用传入对象初始化。
/
//否定二元返回值
template <class Predicate> 
class binary_negate 
  : public binary_function<typename Predicate::first_argument_type,
                           typename Predicate::second_argument_type,
                           bool> {
protected:
  Predicate pred;
public:
  explicit binary_negate(const Predicate& x) : pred(x) {}
  bool operator()(const typename Predicate::first_argument_type& x, 
                  const typename Predicate::second_argument_type& y) const {
    return !pred(x, y); 
  }
};
template <class Predicate>
inline binary_negate<Predicate> not2(const Predicate& pred) {
  return binary_negate<Predicate>(pred);
}
//not1 example
#include <iostream>     // std::cout
#include <functional>   // std::not1
#include <algorithm>    // std::count_if

struct IsOdd {
  bool operator() (const int& x) const {return x%2 == 1;}
  typedef int argument_type;
};//类

int main () {
  int values[] = {1,2,3,4,5};
  int cx = std::count_if( values, values+5, std::not1(IsOdd()) );//找出不是奇数的个数
  //IsOdd()产生临时对象a,not1返回临时对象并用a初始化。
  std::cout << "There are " << cx << " elements with even values.\n";
  return 0;
}

输出:
There are 2 elements with even values.

// not2 example
#include <iostream>     // std::cout
#include <functional>   // std::not2, std::equal_to
#include <algorithm>    // std::mismatch
#include <utility>      // std::pair

int main () {
  int foo[] = {10,20,30,40,50};
  int bar[] = {0,15,30,45,60};

  std::pair<int*,int*> firstmatch,firstmismatch;

  firstmismatch = std::mismatch (foo, foo+5, bar, std::equal_to<int>());//返回第一个不匹配数值

  firstmatch = std::mismatch (foo, foo+5, bar, std::not2(std::equal_to<int>()));//返回第一个匹配的数值

  std::cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
  std::cout << "First match in bar is " << *firstmatch.second << '\n';
  return 0;
}

输出:
First mismatch in bar is 0
First match in bar is 30

2、将仿函数某个参数绑定为固定值的适配器

//绑定参数,将二元函数某个参数绑定为恒定值///

//Operation前面讲解的仿函数类
template <class Operation> 
class binder2nd
  : public unary_function<typename Operation::first_argument_type,
                          typename Operation::result_type> {
protected:
  Operation op;//仿函数对象
  typename Operation::second_argument_type value;//第二个参数类型
public:
  binder2nd(const Operation& x,
            const typename Operation::second_argument_type& y) 
      : op(x), value(y) {}//传入x是对象的引用,第二参数的引用
  typename Operation::result_type
  operator()(const typename Operation::first_argument_type& x) const {//传入x,底层调用op
    return op(x, value);//将value绑定为op的第二个参数 
  }
};

//辅助函数,辅助产生绑定好的对象 bind2nd
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
  typedef typename Operation::second_argument_type arg2_type;
  return binder2nd<Operation>(op, arg2_type(x));//仅仅产生临时对象,可以传给模板函数
}



//第一个参数绑定起来
template <class Operation> 
class binder1st
  : public unary_function<typename Operation::second_argument_type,
                          typename Operation::result_type> {
protected:
  Operation op;//操作
  typename Operation::first_argument_type value;//第一个参数类型
public:
  binder1st(const Operation& x,
            const typename Operation::first_argument_type& y)
      : op(x), value(y) {}//构造
  typename Operation::result_type
  operator()(const typename Operation::second_argument_type& x) const {
    return op(value, x); 
  }
};

//辅助函数调用进行
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& op, const T& x) {
  typedef typename Operation::first_argument_type arg1_type;
  return binder1st<Operation>(op, arg1_type(x));
}
//
// binder2nd example
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;

int main () {
  int numbers[] = {10,-20,-30,40,-50};
  int cx;
  int cx1;
  binder2nd< less<int> > IsNegative (less<int>(),0);//将less<int>重新包装产生新的对象binder2nd
  cx = count_if (numbers,numbers+5 , IsNegative);//二者用法一样
  cx1 = count_if (numbers,numbers+5,bind2nd(less<int>() , 0));
  cout << "There are " << cx <<"  "<< cx1 << " negative elements.\n";
  return 0;
}

输出结果:

There are 3 3 negative elements.

3、将两个仿函数合并成一个仿函数的适配器

///用于函数合成/
//一元仿函数合成操作
//h(x) = f( g(x) )
template <class Operation1, class Operation2>
class unary_compose : public unary_function<typename Operation2::argument_type,
                                            typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
public:
  unary_compose(const Operation1& x, const Operation2& y) : op1(x), op2(y) {}
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x));//类似f(g(x))
  }
};

template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2> compose1(const Operation1& op1, 
                                                      const Operation2& op2) {
  return unary_compose<Operation1, Operation2>(op1, op2);//返回临时对象
}

//二元仿函数合成操作
//h(x) = f( g1(x) , g2(x) )
template <class Operation1, class Operation2, class Operation3>
class binary_compose
  : public unary_function<typename Operation2::argument_type,
                          typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
  Operation3 op3;
public:
  binary_compose(const Operation1& x, const Operation2& y, 
                 const Operation3& z) : op1(x), op2(y), op3(z) { }
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x), op3(x));//返回临时对象
  }
};

template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2, Operation3> 
compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3) {
  return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);
}

4、将函数指针合并成仿函数的适配器

///用于函数指针/

//将一元函数指针包装成仿函数
template <class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result> {
protected:
  Result (*ptr)(Arg);//函数指针变量
public:
  pointer_to_unary_function() {}
  explicit pointer_to_unary_function(Result (*x)(Arg)) : ptr(x) {}
  Result operator()(Arg x) const { return ptr(x); }
};

template <class Arg, class Result>
inline pointer_to_unary_function<Arg, Result> ptr_fun(Result (*x)(Arg)) {
  return pointer_to_unary_function<Arg, Result>(x);//传入函数指针、一元参数类型、返回值类型,返回一个仿函数对象
}

/

//将二元函数指针包装成仿函数
template <class Arg1, class Arg2, class Result>
class pointer_to_binary_function : public binary_function<Arg1, Arg2, Result> {
protected:
    Result (*ptr)(Arg1, Arg2);
public:
    pointer_to_binary_function() {}
    explicit pointer_to_binary_function(Result (*x)(Arg1, Arg2)) : ptr(x) {}
    Result operator()(Arg1 x, Arg2 y) const { return ptr(x, y); }
};

template <class Arg1, class Arg2, class Result>
inline pointer_to_binary_function<Arg1, Arg2, Result> 
ptr_fun(Result (*x)(Arg1, Arg2)) {
  return pointer_to_binary_function<Arg1, Arg2, Result>(x);//返回对象即可
}
// ptr_fun example
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstdlib>
#include <numeric>
using namespace std;

int main () {
  char* foo[] = {"10","20","30","40","50"};
  int bar[5];
  int sum;
  transform (foo, foo+5, bar, ptr_fun(atoi) );//将函数指针转换成仿函数对象,这里输入函数指针效果一样
  transform (foo, foo+5, bar, atoi );
  sum = accumulate (bar, bar+5, 0);
  cout << "sum = " << sum << endl;
  return 0;
}

输出:
sum = 150

5、将成员函数指针提取出来包装成仿函数适配器

//函数指针类别:返回S 无输入  通过指针调用
template <class S, class T>
class mem_fun_t : public unary_function<T*, S> {
public:
  explicit mem_fun_t(S (T::*pf)()) : f(pf) {}//初始化
  S operator()(T* p) const { return (p->*f)(); }//调用,p里面对应的函数
private:
  S (T::*f)();//这是一个变量,这个函数指针变量
};
//辅助函数,直接通过模板萃取相应的型别,然后声明相应的对象
template <class S, class T>
inline mem_fun_t<S,T> mem_fun( S (T::*f)() ) { 
  return mem_fun_t<S,T>(f);//返回仿函数临时对象,真的很牛逼哦抽象出来了
}
//小例子
size_type length() const 
{ return _M_string_length; }
mem_fun(&string::length);//传入函数指针,::优先级大于&
//s 是 size_type  T是string  f是length,通过模板萃取出这些型别,即可产生相应的对象。


//有一个参数,通过指针调用
template <class S, class T, class A>
class mem_fun1_t : public binary_function<T*, A, S> {
public:
  explicit mem_fun1_t(S (T::*pf)(A)) : f(pf) {}
  S operator()(T* p, A x) const { return (p->*f)(x); }
private:
  S (T::*f)(A);
};

template <class S, class T, class A>
class const_mem_fun1_t : public binary_function<const T*, A, S> {
public:
  explicit const_mem_fun1_t(S (T::*pf)(A) const) : f(pf) {}
  S operator()(const T* p, A x) const { return (p->*f)(x); }
private:
  S (T::*f)(A) const;
};
// mem_fun example
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

int main () {
  vector <string*> numbers;

  // populate vector of pointers:
  numbers.push_back ( new string ("one") );
  numbers.push_back ( new string ("two") );
  numbers.push_back ( new string ("three") );
  numbers.push_back ( new string ("four") );
  numbers.push_back ( new string ("five") );

  vector<int> lengths(numbers.size());//预先分配内存空间

  transform (numbers.begin(), numbers.end(), lengths.begin(), mem_fun(&string::length));

  for (int i=0; i<5; i++) {
    cout << *numbers[i] << " has " << lengths[i] << " letters.\n";
  }

  // deallocate strings:
  for (vector<string*>::iterator it = numbers.begin(); it!=numbers.end(); ++it)
    delete *it;

  return 0;
}

输出:
one has 3 letters.
two has 3 letters.
three has 5 letters.
four has 4 letters.
five has 4 letters.

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Cold_Sun_/article/details/101078365

智能推荐

如何删除基址的重定位表----记一次对RAR软件的基址重定位删除实践-程序员宅基地

文章浏览阅读287次。工具: 1.PETool 2.HxD 步骤: 1.PE查看rar程序的结构: 重定位的具体信息如下: 2.删除.reloc的节区头 从地址278开始,删除到29c+3 这部分清零。 3.删除.reloc节区 ..._减少重定位表

应聘软件测试岗位需要掌握的基础知识与技能(面试常考内容)_测试工程师需要哪些知识储备-程序员宅基地

文章浏览阅读1.8w次,点赞212次,收藏676次。详细讲解应聘软件测试岗位所需要掌握的一些列基础知识与技能,包括软件测试的基本概念和基本流程、基础的网络知识、常用的数据库技能以及常用的Linux命令等。_测试工程师需要哪些知识储备

如何下载道客巴巴付费文档_万能文库下载器,一键下载付费文档-程序员宅基地

文章浏览阅读1.1w次。Photoshop视频教程1000G免费资源整合/仅限100个名额领取■来源:晚安夕颜 ID:iiixiyan终身会员办理今天小编为你精选了万能文库下载区支持游览器版本:360游览器,IE,,,支持系统:win/Mac获取方式:添加小编微信「XiaoBian08021314」注意大小写分享转发群免费获取自我困扰区对于学生党来说,经常会用到如百度文库、豆丁网等网站下载文档资料。特别是即..._如何下载道客巴巴收费ppt

c语言FD_SET头文件,select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET-程序员宅基地

文章浏览阅读2.1k次。―――――――――――――――――――――――――――――――――――――――2、select函数的接口比较简单:int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct tim *timeout);功能:测试指定的fd可读?可写?有异常条件待处理?参数:nfds需要检查的文件描述字个数(即检查..._c fd_set

他一年开发19款!款款口碑爆棚-程序员宅基地

文章浏览阅读171次。前言转眼一年又到了最后的十几天了,这一年发生了很多事情,其中很重要的一件事,就是从去年年底开始,在业余时间编写上架各种Creator插件。借着这个特殊的时间节点,正好来回顾下,并把文中..._ssrshadertoy

HDFS、YARN、MapReduce概述及三者之间的关系_hdfs mapreduce yarn-程序员宅基地

文章浏览阅读7.9k次,点赞12次,收藏52次。一、HDFS架构概述1、NameNode(nn): 存储文件的元数据,如文件名、文件目录结构、文件属性(生成时间、副本数、文件权限),以及每个文件的块列表和块所在的DataNode等。2nn:每隔一段时间对NameNode元数据备份..._hdfs mapreduce yarn

随便推点

memcache视频java,MemCache视频教程-程序员宅基地

文章浏览阅读1.4k次。@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);ed2k://|file|%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2PH...

php 7.4连接MySQL_php7.4 mysql-程序员宅基地

文章浏览阅读1.3w次,点赞15次,收藏80次。准备工作:安装php的IDE,如PhpStorm下载php并解压,官网:https://www.php.net/downloadsPhpStorm配置php开发环境1.1 打开PhpStorm,点击File->Settings:1.2 点击"Languages & Frameworks”,找到PHP1.3选择php版本,选择CLI Interpreter(客户端..._php7.4 mysql

NOP指令概述及作用_linux nop命令怎么写-程序员宅基地

文章浏览阅读1.6w次,点赞6次,收藏9次。计算机科学中,NOP或NOOP(No Operation或No Operation Performed的缩写,意为无操作)是汇编语言的一个指令,一系列编程语句,或网络传输协议中的表示不做任何有效操作的命令。NOP是用执行一条具有操作数,具有相同效果的指令;NOP指令通常用于控制时序的目的,强制内存对齐,防止流水线灾难,占据分支指令延迟),或是作为占位符以供程序的改善(或替代被移除的指令)。_linux nop命令怎么写

arm-从0开始刷机(linux下)-程序员宅基地

文章浏览阅读1.2k次。环境1.操作系统:deepin 5.3.152.代码编辑器:Visual Studio Code3.

boost.asio系列(一)——deadline_timer-程序员宅基地

文章浏览阅读71次。一.构造函数  一个deadline_timer只维护一个超时时间,一个deadline_timer不同时维护多个定时器。在构造deadline_timer时指定时间:1 basic_deadline_timer(boost::asio::io_service & io_service);2 3 basic_deadline_timer( boost::asio::io..._boost asio deadline lamdba

VCS 加速编译的方法——VCS Partition Compile_vcs加速编译-程序员宅基地

文章浏览阅读8.2k次,点赞9次,收藏112次。文章目录前言1 Partition Compile2 Autopartitioning(Ease of Adoption)3 Specifying Partitions Manually(Recommended)3.1 topcfg.v file3.2 Two step commands for partition compile3.3 Three step commands for partition compile3.4 Profiling of Compilation Time4 Best Pract_vcs加速编译

推荐文章

热门文章

相关标签