17

我无法理解使用 Boost.Phoenix 的真正好处。

当我将它与 Boost.Spirit 语法一起使用时,它真的很有用:

double_[ boost::phoenix::push_back( boost::phoenix::ref( v ), _1 ) ]

当我将它用于 lambda 函数时,它也很有用且优雅:

boost::range::for_each( my_string, if_ ( '\\' == arg1 ) [ arg1 = '/' ] );

但是这个库中其他所有东西的好处是什么?文档说:“无处不在的函子”。我不明白这有什么好处?

4

5 回答 5

13

我会指出 Boost.Lambda 和 Boost.Phoenix 之间的关键区别是什么:

Boost.Phoenix 支持(静态)多态函子,而 Boost.Lambda 绑定总是单态的。

(同时,这两个库在很多方面是可以组合的,所以它们不是排他性的选择。)

让我说明一下(警告:代码未测试。)

凤凰

在 Phoenix 中,仿函数可以转换为 Phoenix“惰性函数”(来自http://www.boost.org/doc/libs/1_54_0/libs/phoenix/doc/html/phoenix/starter_kit/lazy_functions.html

struct is_odd_impl{
    typedef bool result_type; // less necessary in C++11
    template <typename Arg>
    bool operator()(Arg arg1) const{
        return arg1 % 2 == 1;
    }
};

boost::phoenix::function<is_odd_impl> is_odd;

is_odd是真正的多态(作为函子is_odd_impl)。那就是is_odd(_1)可以作用于任何东西(这是有道理的)。例如在is_odd(_1)(2u)==true和中is_odd(_1)(2l)==trueis_odd可以组合成更复杂的表达式,而不会失去其多态行为。

Lambda 尝试

在 Boost.Lambda 中我们最接近这个的是什么?我们可以定义两个重载:

bool is_odd_overload(unsigned arg1){return arg1 % 2 == 1;}
bool is_odd_overload(long     arg1){return arg1 % 2 == 1;}

但是要创建一个 Lambda “惰性函数”,我们必须选择以下两者之一:

using boost::lambda::bind;
auto f0 = bind(&is_odd_overload, _1); // not ok, cannot resolve what of the two.
auto f1 = bind(static_cast<bool(*)(unsigned)>(&is_odd_overload), _1); //ok, but choice has been made
auto f2 = bind(static_cast<bool(*)(long)>(&is_odd_overload), _1); //ok, but choice has been made

即使我们定义了一个模板版本

template<class T>
bool is_odd_template(T arg1){return arg1 % 2 == 1;}

例如,我们必须绑定到模板函数的特定实例

auto f3 = bind(&is_odd_template<unsigned>, _1); // not tested

既不f1f2也不f3是真正的多态,因为在绑定时已经做出了选择。

(注 1:这可能不是最好的例子,因为从 unsigned 到 long 的隐式转换,事情似乎可以正常工作,但这是另一回事。)

总而言之,给定一个多态函数/仿函数 Lambda 不能绑定到多态函数(据我所知),而 Phoenix 可以。确实,Phoenix 依赖于“协议结果” http://www.boost.org/doc/libs/1_54_0/libs/utility/utility.htm#result_of但 1)至少有可能,2)这在 C++11 中问题不大,返回类型很容易推断并且可以自动完成。

事实上,在 C++11 中,Phoenix lambdas 仍然比 C++11 内置的 lambdas 更强大。即使在实现模板泛型 lambda 的 C++14 中,Phoenix 仍然更通用,因为它允许一定程度的自省。(为此,Joel de Guzman(Phoenix 的开发人员)曾经并且仍然远远领先于他的时代。)

于 2011-02-17T05:48:21.523 回答
5

嗯,它是一种非常强大的 lambda 语言。

我用它为类似数学的 DSL 创建了一个原型:

http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/interval.hpp

和许多其他事情:

http://code.google.com/p/asadchev/source/browse/#svn%2Ftrunk%2Fprojects%2Fboost%2Fphoenix

于 2011-02-16T07:30:42.983 回答
4

我从来没有用过凤凰,但是...

来自凤凰图书馆文档

Phoenix 库支持 FP 技术,例如 C++ 中的高阶函数、lambda(未命名函数)、currying(部分函数应用程序)和惰性求值

来自关于函数式编程的维基百科文章

...函数式编程是一种编程范式,将计算视为对数学函数的评估,并避免状态和可变数据。它强调函数的应用,与强调状态变化的命令式编程风格相反

因此,Phoenix 是一个用于在 C++ 中启用函数式编程的库。

如今,函数式编程的主要兴趣似乎源于由于限制或消除副作用而在正确性和性能方面的感知优势。

正确性,因为没有副作用,您看到的代码就是系统中发生的一切。其他一些代码不会改变你下面的状态。在这种环境中,您可以更轻松地编写无错误代码。

性能,因为没有副作用,您编写的代码可以安全地并行运行,无需任何资源管理原语或原子访问技巧。多线程可以非常容易地启用,甚至可以自动启用,并且可以非常高效地运行。

于 2011-02-16T07:21:53.103 回答
3

不要看Boost.Phoenix2。

boost 中 lambda 表达式的演变如下所示:

绑定 -> Lambda,Phoenix2(作为 Spirit 部分)-> Phoenix3(作为单独的库,正在开发中)。

结果是具有多态仿函数支持的单个 lambda 库(其他将被弃用)。

于 2011-02-28T17:28:30.390 回答
1

C++ 中的函数式编程。除非您以前使用过适当支持函数式编程的语言,例如 SML,否则很难解释。我尝试使用 Phoenix,发现它很好,但在实际项目中非常不切实际,因为它大大增加了编译时间,而且当你做错事时错误消息很糟糕。我记得我在玩 Phoenix 时从 GCC 收到了几兆字节的错误。此外,调试深度嵌套的模板实例化是一个 PITA。(实际上,这些也是反对使用大部分 boost 的所有论据。)

于 2011-02-16T07:13:55.927 回答