C++ Boost Phoenix库示例分析使用

   2023-02-09 学习力0
核心提示:目录一、说明二、预先知道Boost.Phoenix三、示例和代码一、说明在函数式编程模型中,函数是对象,与其他对象一样,可以作为参数传递给函数或存储在容器中。有许多支持函数式编程模型的 Boost 库。Boost.Phoenix 是这些库中最广泛、也是最重要的库。它取代了库

一、说明

在函数式编程模型中,函数是对象,与其他对象一样,可以作为参数传递给函数或存储在容器中。有许多支持函数式编程模型的 Boost 库。

  • Boost.Phoenix 是这些库中最广泛、也是最重要的库。它取代了库 Boost.Lambda,它被简要介绍,但只是为了完整性。
  • Boost.Function 提供了一个类,可以轻松定义函数指针,而无需使用源自 C 编程语言的语法。
  • Boost.Bind 是一个适配器,即使实际签名与预期签名不同,它也允许您将函数作为参数传递给其他函数。
  • Boost.Ref 可用于传递对对象的引用,即使函数通过副本传递参数。
  • Boost.Lambda 可以称为 Boost.Phoenix 的前身。它是一个相当古老的库,并且在将 C++11 添加到编程语言之前很多年就允许使用 lambda 函数。

二、预先知道Boost.Phoenix

Boost.Phoenix 是函数式编程最重要的 Boost 库。虽然 Boost.Bind 或 Boost.Lambda 等库为函数式编程提供了一些支持,但 Boost.Phoenix 包含这些库的功能并超越了它们。

在函数式编程中,函数是对象,可以像对象一样处理。使用 Boost.Phoenix,一个函数可以返回另一个函数作为结果。也可以将一个函数作为参数传递给另一个函数。因为函数是对象,所以可以区分实例化和执行。访问一个函数不等于执行它。

Boost.Phoenix 支持使用函数对象进行函数式编程:函数是基于类的对象,这些类重载了运算符 operator()。这样,函数对象的行为就像 C++ 中的其他对象一样。例如,它们可以被复制并存储在容器中。但是,它们的行为也类似于函数,因为它们可以被调用。

函数式编程在 C++ 中并不新鲜。您可以将一个函数作为参数传递给另一个函数,而无需使用 Boost.Phoenix。

三、示例和代码

示例 39.1。谓词作为全局函数、lambda 函数和 Phoenix 函数

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd(int i) { return i % 2 == 1; }
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  std::cout << std::count_if(v.begin(), v.end(), is_odd) << '\n';
  auto lambda = [](int i){ return i % 2 == 1; };
  std::cout << std::count_if(v.begin(), v.end(), lambda) << '\n';
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.1

示例 39.1 使用算法 std::count_if() 来计算向量 v 中的奇数。std::count_if() 被调用 3 次,一次使用谓词作为独立函数,一次使用 lambda 函数,一次使用凤凰功能。

Phoenix 函数与独立函数和 lambda 函数不同,因为它没有框架。虽然其他两个函数有一个带有签名的函数头,但 Phoenix 函数似乎只包含一个函数体。

Phoenix 函数的关键组件是 boost::phoenix::placeholders::arg1。 arg1 是函数对象的全局实例。您可以像 std::cout 一样使用它:一旦包含相应的头文件,这些对象就会存在。

arg1 用于定义一元函数。表达式 arg1 % 2 == 1 创建一个需要一个参数的新函数。该函数不会立即执行,而是存储在 Phoenix 中。 phoenix 被传递给 std::count_if() ,它为 v 中的每个数字调用谓词。

arg1 是调用 Phoenix 函数时传递的值的占位符。由于此处仅使用了 arg1,因此创建了一个一元函数。 Boost.Phoenix 提供了额外的占位符,例如 boost::phoenix::placeholders::arg2 和 boost::phoenix::placeholders::arg3。 Phoenix 函数总是期望与具有最大数量的占位符一样多的参数。

Example39.1

示例 39.1 将 3 写入标准输出 3 次。

示例 39.2。 Phoenix 函数与 lambda 函数

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  auto lambda = [](int i){ return i % 2 == 1; };
  std::cout << std::count_if(v.begin(), v.end(), lambda) << '\n';
  std::vector<long> v2;
  v2.insert(v2.begin(), v.begin(), v.end());
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
  std::cout << std::count_if(v2.begin(), v2.end(), phoenix) << '\n';
}

Example39.2

例 39.2 强调了 Phoenix 和 lambda 函数之间的一个关键区别。除了不需要带有参数列表的函数头之外,Phoenix 函数参数没有类型。 lambda 函数 lambda 需要一个 int 类型的参数。 Phoenix 函数 phoenix 将接受模运算符可以处理的任何类型。

将 Phoenix 函数视为函数模板。像函数模板一样,Phoenix 函数可以接受任何类型。这使得在示例 39.2 中可以使用 phoenix 作为容器 v 和 v2 的谓词,即使它们存储不同类型的数字。如果您尝试将谓词 lambda 与 v2 一起使用,则会出现编译器错误。

示例 39.3。 Phoenix 用作延迟 C++ 代码

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 > 2 && arg1 % 2 == 1;
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.3

示例 39.3 使用 Phoenix 函数作为谓词,使用 std::count_if() 计算大于 2 的奇数。Phoenix 函数访问 arg1 两次:一次测试占位符是否大于 2,一次测试它是否为奇数.条件与 && 相关联。

您可以将 Phoenix 函数视为不会立即执行的 C++ 代码。示例 39.3 中的 Phoenix 函数看起来像一个使用多个逻辑和算术运算符的条件。但是,条件不会立即执行。它仅在从 std::count_if() 中访问时执行。 std::count_if() 中的访问是正常的函数调用。

示例 39.3 将 2 写入标准输出。

示例 39.4。显式 Phoenix 类型

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  auto phoenix = arg1 > val(2) && arg1 % val(2) == val(1);
  std::cout << std::count_if(v.begin(), v.end(), phoenix) << '\n';
}

Example39.4

例 39.4 对 Phoenix 函数中的所有操作数使用显式类型。严格来说,你看不到类型,只有辅助函数 boost::phoenix::val()。此函数返回使用传递给 boost::phoenix::val() 的值初始化的函数对象。函数对象的实际类型无关紧要。重要的是 Boost.Phoenix 为不同类型重载了运算符,如 >、&&、% 和 ==。因此,不会立即检查条件。相反,函数对象被组合以创建更强大的函数对象。根据操作数,它们可能会自动用作函数对象。否则,您可以调用 val() 等辅助函数。

示例 39.5。 boost::phoenix::placeholders::arg1 和 boost::phoenix::val()

#include <boost/phoenix/phoenix.hpp>
#include <iostream>
int main()
{
  using namespace boost::phoenix::placeholders;
  std::cout << arg1(1, 2, 3, 4, 5) << '\n';
  auto v = boost::phoenix::val(2);
  std::cout << v() << '\n';
}

Example39.5

示例 39.5 说明了 arg1 和 val() 如何工作。 arg1 是函数对象的实例。它可以直接使用,也可以像函数一样调用。您可以传递任意数量的参数——arg1 返回第一个参数。

val() 是一个用于创建函数对象实例的函数。函数对象使用作为参数传递的值进行初始化。如果实例像函数一样被访问,则返回该值。

示例 39.5 将 1 和 2 写入标准输出。

示例 39.6。创建自己的 Phoenix 函数

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
struct is_odd_impl
{
    typedef bool result_type;
 
    template <typename T>
    bool operator()(T t) const { return t % 2 == 1; }
};
boost::phoenix::function<is_odd_impl> is_odd;
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), is_odd(arg1)) << '\n';
}

Example39.6

示例 39.6 解释了如何创建自己的 Phoenix 函数。您将函数对象传递给模板 boost::phoenix::function。该示例传递了 is_odd_impl 类。此类重载运算符 operator():当传入奇数时,运算符返回 true。否则,运算符返回 false。

请注意,您必须定义类型result_type。 Boost.Phoenix 使用它来检测运算符 operator() 的返回值的类型。

is_odd() 是一个可以像 val() 一样使用的函数。两个函数都返回一个函数对象。调用时,参数将转发给运算符 operator()。对于示例 39.6,这意味着 std::count_if() 仍然计算奇数。

示例 39.7。将独立功能转换为 Phoenix 功能

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd_function(int i) { return i % 2 == 1; }
BOOST_PHOENIX_ADAPT_FUNCTION(bool, is_odd, is_odd_function, 1)
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), is_odd(arg1)) << '\n';
}

如果要将独立函数转换为 Phoenix 函数,可以按照示例 39.7 进行操作。您不必像前面的示例中那样定义函数对象。

您可以使用宏 BOOST_PHOENIX_ADAPT_FUNCTION 将独立函数转换为 Phoenix 函数。将返回值的类型、要定义的 Phoenix 函数的名称、独立函数的名称以及参数个数传递给宏。

示例 39.8。 Phoenix 使用 boost::phoenix::bind() 函数

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd(int i) { return i % 2 == 1; }
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  std::cout << std::count_if(v.begin(), v.end(), bind(is_odd, arg1)) << '\n';
}

要将独立函数用作 Phoenix 函数,您还可以使用 boost::phoenix::bind(),如示例 39.8 中所示。 boost::phoenix::bind() 的工作方式类似于 std::bind()。独立函数的名称作为第一个参数传递。所有进一步的参数都被转发到独立功能。

小费
避免使用 boost::phoenix::bind()。创建您自己的 Phoenix 函数。这会导致代码更具可读性。尤其是对于复杂的表达式,处理 boost::phoenix::bind() 的额外细节是没有帮助的。

示例 39.9。任意复杂的 Phoenix 函数

#include <boost/phoenix/phoenix.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  using namespace boost::phoenix;
  using namespace boost::phoenix::placeholders;
  int count = 0;
  std::for_each(v.begin(), v.end(), if_(arg1 > 2 && arg1 % 2 == 1)
    [
      ++ref(count)
    ]);
  std::cout << count << '\n';
}

Boost.Phoenix 提供了一些模拟 C++ 关键字的函数对象。例如,您可以使用函数 boost::phoenix::if_()(参见示例 39.9)创建一个函数对象,该对象的行为类似于 if 并测试条件。如果条件为真,则使用 operator[] 传递给函数对象的代码将被执行。当然,该代码也必须基于函数对象。这样,您可以创建复杂的 Phoenix 函数。

示例 39.9 对每个大于 2 的奇数进行增量计数。要对 count 使用增量运算符,请使用 boost::phoenix::ref() 将 count 包装在一个函数对象中。与 boost::phoenix::val() 相比,没有值被复制到函数对象中。 boost::phoenix::ref() 返回的函数对象存储了一个引用——这里是对 count 的引用。

小提示

不要使用 Boost.Phoenix 创建复杂的函数。最好使用 C++11 中的 lambda 函数。虽然 Boost.Phoenix 接近 C++ 语法,但使用 if_ 等关键字或方括号之间的代码块并不一定会提高可读性。

原文地址:https://yamagota.blog.csdn.net/article/details/127702549
 
标签: C++ Boost Phoenix
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Aurelius vs mORMot vs EntityDAC Delphi 的
    Aurelius vs mORMot vs EntityDAC   Delphi 的 ORM框架:http://www.tmssoftware.com/site/aurelius.asp#product-buy-onlinehttps://synopse.info/fossil/wiki/Synopse+OpenSourcehttps://www.devart.com/entitydac/download.htmlkbmMW  http://www.compo
    02-09
  • 【Ruby】Mac gem的一些坑
    前言自上一次升级MacOS系统后出现jekyll无法构建的问题,当时处理半天。谁知道最近又升级了MacOS,荒废博客多时,今天吝啬写了一篇准备发布,构建报错,问题重新。还是记录下,以防下次升级出问题。问题描述安装jekyll静态博客需要在Ruby环境下运行,于是参照
    02-09
  • iOS oc 调用 swift
    如股票oc要调用swift里面的代码 需要包含固定这个头文件项目名称 LiqunSwiftDemo-Swift.h         #ProjectName#-Swift.h固定的写法swift 目的 是取代oc 但是 不会完全取代 只是前端的替换LiqunSwiftDemo-Swift 点进去 可以看到 所有的swift代码 都产生
    02-09
  • objective-c NSTimer 定时器
    -(void)initTimer{//时间间隔NSTimeInterval timeInterval =3.0 ;//定时器repeats 表示是否需要重复,NO为只重复一次NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(mobileAnimation) userInfo:nil
    02-09
  • Objective-C  日记③ 字符串
    Objective-C 日记③ 字符串
    一、创建字符串、类方法   公式创建NSString  +(id) stringWithFormat:(NSString *) format,……;eg:  NSString *height;  height=[NSString stringWithFormat:@"高度是: %d 长度: %d",10,20];得到的字符串:“高度是: 10 长度: 20” 注意:  省
    02-09
  • Objective-C KVC机制
    Objective-C KVC机制http://blog.csdn.net/omegayy/article/details/7381301全部推翻重写一个版本,这是我在公司内做技术分享的文档总结,对结构、条理做了更清晰的调整。 1.    基本概念MODEL主要是英文文档里面经常出现的一些概念,讲解一下,方便英文
    02-09
  • objective-c 加号 减号 - +
    “加号代表static”是错误的说法,可能跟你那样表达的人其实意思是:“前置加号的方法相当于Java 里面的静态方法”。在Oc中,方法分为类方法和实例方法。前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。有人把
    02-09
  • Objective-C  日记①
    Objective-C 日记①
    1、Xcode的.m扩展名表示文件含有Object-C代码,C以.c文件,C++以.cpp文件2、头文件声明:C使用:#include,O-C使用#import(当然你也可以使用#include) 3、输出方式:  C:printf("",参数);  O-C:NSLog(@"",参数); 4、布尔类型  C:bool 具有true
    02-09
  • ASP.NET MVC 操作AD 获取域服务器当前用户姓
    #region 根据当前登录域账号 获取AD用户姓名和所在OU目录/// summary/// 根据当前登录域账号 获取AD用户姓名和所在OU目录/// /summary/// param name="searchUser"要搜索的当前用户名/param/// param name="paths"out返回该用户所在OU目录/param/// param nam
    02-09
  • swift和OC - 拆分数组 和 拆分字符串
    1. 拆分数组 /// 根据 数组 截取 指定个数返回 多个数组的集合func splitArray( array: [Date], withSubSize subSize: Int) - [[Date]] {//数组将被拆分成指定长度数组的个数let count = array.count% subSize == 0 ? (array.count/ subSize) : (array.count
    02-08
点击排行