C++ 方法引用

2021年01月14日 阅读数:7
这篇文章主要向大家介绍C++ 方法引用,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
C++ feels like a new language. ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ-- Bjarne Stroustrup

说在前面

今天的标题可能不太严谨,这里我借用了Java语言中的说法,熟悉Java的朋友应该都知道方法引用的含义,这里引入Java 8 in action中的一段代码c++

inventory.sort(comparing(Apple::getWeight));

这里的Apple::getWeight就是咱们说的方法引用,这里能够理解为咱们将Apple类的方法getWeight传递给了comparing,将函数做为一等公民进行传递。git

  • 正题

更准确地说,咱们如何将成员方法应用到STL算法上?先看一个例子github

struct Cell {
  explicit Cell(int value) : value_(value) {}
  int get_value() const { return value; }

 private:
   int value_;
};

应用算法

std::vector<Cell> inputs = {Cell(3), Cell(10), Cell(113)};
std::vector<int> outputs;
  
std::transform(begin(inputs), end(inputs), back_inserter(outputs), Cell::get_value); //compile error!

咱们知道上面的代码是没法经过编译的,那么问题来了,在C++中怎么实现相似的东西。函数

昂贵的解决方案 std::function

咱们可使用C++2.0引入的std::function解决上述没法经过编译的问题工具

std::transform(begin(inputs), end(inputs), std::back_inserter(outputs),
               std::function<int(const Cell&)>(&Cell::get_value));

std::function接受几乎全部可调用的东西(自由函数,成员函数,静态函数,函数对象),并将其包装在定义一个operator()的对象中,该对象将调用转发给包装的可调用对象。
对于不太清楚std::function和可调用对象的读者能够参考个人另一篇文章可调用对象网站

次优的解决方案 lambda

一种简单的方法就是将成员函数包装在lambda指针

std::transform(begin(inputs), end(inputs), back_inserter(outputs),
               [](const Cell& input) { return input.get_value(); });

这里说lambda表达式的实现确实简单有效,并且正确,我的认为这么去作是没有任何问题的,说它是次优解是由于引入了一个新的变量。code

最适合的工具 std::mem_fn

针对本例,最适合的工具就是std::mem_fn了。它是C++2.0以后引入的,能够取代std::mem_funstd::mem_fun_reform

std::transform(begin(inputs), end(inputs), back_inserter(outputs), std::mem_fn(&Cell::get_value));

上述的写法看起来跟开篇将到的Java的例子就很相似了。
简单介绍一下std::mem_fnstd::mem_funstd::mem_fun_ref

  • 引入时间

std::mem_fn:C++11之后
std::mem_funstd::mem_fun_ref:C++98标准

  • 区别

std::mem_fun是指针版的把成员函数转换成函数对象。
std::mem_fun_ref是引用版的把成员函数转换成函数对象。
std::mem_fn则是不管指针仍是引用均可以把成员函数转换为函数对象。

  • 头文件

#include <functional>

使用 range

C++20引入了range库的概念,咱们看看怎么用

auto outputs = inputs | ranges::view::transform(&Cell::get_value); // OK

不过遗憾的是,目前主流的C++编译器还不支持这种写法。不过读者能够引入range-v3,用法很是简单,上面的代码不用修改,只要将range-v3的头文件引入到工程中去,有兴趣的读者能够自行去研究range-v3这个库。

后记

对于range有兴趣的读者能够参考这个网站ranges