C++ Magic Collection


嘛……最近这个博客好久没有写干货了。正好今晚有空 可以摸摸鱼,就来说说最近Cpp的一些东西吧。果然Cpp是世界上最复杂的语言(x

R-value 运算符

C++11之后,可以利用

来针对左值和右值对象重载不同版本的运算符。典型例子就是

如果不写左值和右值不同的版本,那么:

T obj2(++std::move(obj1));

会退化到使用左值版本的构造函数,造成性能损失。

但是,在

https://www.zhihu.com/question/38851602/answer/78421911?group_id=663734370451853312#comment-110278512

有人提到了,根本就不应该对右值提供这类运算符,对此个人持保留意见。而且,对于foo(++bar())这类语法,如果使用foo(bar() + 1) T tmp = bar(); ++tmp; foo(tmp);等语法都会造成foo退化到左值版本,观望中。

前C++11年代的函数接口

众所周知,C++11引入了std::function,针对不同类型的可调用对象,提供了统一的接口。那么,在C++11之前,函数的接口是怎么样的呢?

警告:这部分内容在C++11中已经被标为deprecated, 并在C++17中正式被移除,因此请勿在工程中使用这部分内容

因为没有参数包,所以函数接口肯定只能限制在有限个参数。而最有意义的接口有两个:映射函数ret_t map(original_t)和二元函数ret_t comp(original1_t, original2_t)。在C++98的年代,要想实现函数接口的多态性,有两种方式:

一种方式是定义一个原始的ret_t (*FuncType)(original_t)类型,通过给FuncType类型的变量赋不同函数指针的值,来实现函数接口的方式;

另一种就是(1)(动态绑定)构造一个继承自一个有virtual ret_t operator()(original_t) = 0类的Functor,然后定义这个类的operator(),保存父类,通过operator()的多态性来实现函数接口。或(2)(静态绑定)父类typedef xxx arg_1_t; typedef yyy ret_t;,然后子类继承父类实现operator()来提供函数接口,这些过程全部在编译期完成。

不难发现,Functor可以看作一个闭包,而函数指针不能,因此C++98采取了后一种策略,而且提供的是(2)的静态绑定。

对应两种接口,C++定义了以下两个父类:

有了这些接口之后,很容易就能定义出一些关于函数的操作:

除此之外,为了方便从函数构造Functor, 标准库还提供了函数ptr_fun,接受一个unary function 或者一个binary function的函数指针,并返回一个对应std::unary_function或者std::binary_function的Functor:

普通的函数可以直接not2,因为函数指针本身也满足()运算符的约束。但要想把它装起来,还是需要ptr_fun才能做到,因为binary_functon没有函数指针的构造函数。然而,在静态绑定下,把binary_function装起来并没有什么用,因为它不具有operator()

ptr_fun产生的类叫做pointer_to_binary_function<Arg1, Arg2, Result>,就像一个非常原始的std::function<Result(Arg1,Arg2)>一样,是函数指针的简陋封装,比较naive。

然后为了支持成员函数指针,还拥有了一个叫做mem_fun的函数:

它的返回值是mem_fun_t,保存了一个成员函数指针。在使用时,Result operator() (class_t*),需要传入一个对象的指针。同样,对于需要一个参数的成员函数,还有mem_fun_1函数和对应的mem_fun_1_t,构造方式大致相同.

简陋的绑定:只支持binary_functionunary_function的绑定,通过binder1stbinder2nd两个类模板来实现,非常的simple。

最后讨论一下C++98这种做法的不足:

因此,C++11的std::function是一个更加完善的东西。为了兼容性当然也可以使用boost::function

写了这么多,都是在为下一篇《分析std::function》做准备,不过可能要在很久之后了。