Boost Spirit中Lambda配合Phoenix Bind编译异常问题咨询
Boost Spirit Phoenix Bind 与 Lambda 编译问题解析
问题现象
- 带捕获的Lambda无法配合
boost::phoenix::bind编译通过 - 无捕获Lambda前添加
+号后,即可正常配合bind使用
无法编译的代码示例
_val = boost::phoenix::bind( [&](const double _d) { return m_sFactory->value(_d); }, qi::_1 )
可正常编译的代码示例
_val = boost::phoenix::bind( +[](const factory&_r, const double _d) { return _r.value(_d); }, *m_sFactory, qi::_1 )
编译报错信息
user@txa-user-7560:/mnt/c/Users/user/Documents/hierarchy/spectre_parser/spectre_parser$ clang++-15 -std=c++11 spectre_parser.cpp -lpthread|& tee make.out In file included from spectre_parser.cpp:6: ./spectre_parser.h:238:11: error: no matching function for call to 'bind' _val = boost::phoenix::bind( ^~~~~~~~~~~~~~~~~~~~ spectre_parser.cpp:14:54: note: in instantiation of member function 'afs::spectre_parser::name_value_pairs<const char *>::name_value_pairs' requested here afs::spectre_parser::name_value_pairs<const char*> sGrammar(sFactory); ^ /usr/local/include/boost/phoenix/bind/bind_function.hpp:58:5: note: candidate template ignored: could not match 'RT (*)(T...)' against '(lambda at ./spectre_parser.h:240:5)' bind(RT (*f)(T...), A const&... a) ^ 1 error generated. user@txa-user-7560:/mnt/c/Users/user/Documents/hierarchy/spectre_parser/spectre_parser$
原因分析
- Boost 1.80版本的Phoenix
bind函数仅支持普通函数指针作为可调用对象 - 带捕获的Lambda无法隐式转换为函数指针,因此无法匹配
bind的模板参数 - 无捕获Lambda可以隐式转换为函数指针,
+号是显式触发该转换的写法(等价于static_cast<对应函数指针类型>(lambda))
解决方案
- 对于需要访问外部变量的场景,将变量作为参数传递给无捕获Lambda,如示例中把
*m_sFactory作为bind的额外参数传入 - 升级Boost版本至支持捕获Lambda的新版本(较新的Phoenix已兼容各类可调用对象,包括捕获Lambda、仿函数等)
- 使用Phoenix原生Lambda表达式替代标准C++ Lambda,避免兼容性问题
可复现代码
#include <boost/spirit/include/qi.hpp> #include <boost/phoenix/bind/bind_function.hpp> #include <boost/phoenix/operator.hpp> #include <boost/flyweight.hpp> #include <boost/flyweight/set_factory.hpp> #include <iostream> #include <vector> #include <string> #include <utility> namespace parser { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; template<typename T> struct compare; typedef std::pair<std::string, bool> stringAndBool; typedef boost::flyweights::flyweight<stringAndBool, boost::flyweights::set_factory<compare<stringAndBool>> > STRINGFW; template<> struct compare<stringAndBool> { bool operator()(const stringAndBool&_r0, const stringAndBool&_r1) const { return _r0 < _r1; } }; typedef std::vector<STRINGFW> vectorOfStringAndBool; template<> struct compare<vectorOfStringAndBool> { bool operator()(const vectorOfStringAndBool&_r0, const vectorOfStringAndBool&_r1) const { return _r0 < _r1; } }; typedef boost::flyweights::flyweight<vectorOfStringAndBool, boost::flyweights::set_factory<compare<vectorOfStringAndBool>> > vectorOfStringAndBoolFW; template<typename IT> struct skipper:qi::grammar<IT> { skipper(void) :skipper::base_type(start) { } qi::rule<IT> start = ( ("\" " >> qi::eol) | +(qi::blank - qi::eol) | ("//" >> *(qi::char_ - qi::eol)) | (qi::eol >> '+') | (qi::eol >> '*' >> *(qi::char_ - qi::eol) >> qi::eol) | (qi::eol >> *(qi::space - qi::eol) >> qi::eol) ); }; template<typename IT> struct node:qi::grammar<IT, STRINGFW(), skipper<IT> > { qi::rule<IT, STRINGFW(), skipper<IT> > m_sStart; node(void) :node::base_type(m_sStart, "node") { m_sStart = qi::lexeme[+(ascii::alnum | qi::char_('_'))][ qi::_val = boost::phoenix::bind( +[](const std::vector<char>&_r) { return STRINGFW(std::make_pair(std::string(_r.begin(), _r.end()), true)); }, qi::_1 ) ]; m_sStart.name("node"); BOOST_SPIRIT_DEBUG_NODE(m_sStart); } }; template<typename IT> struct nodes_grammar:qi::grammar<IT, vectorOfStringAndBoolFW(), skipper<IT> > { node<IT> m_sNode; qi::rule<IT, vectorOfStringAndBoolFW(), skipper<IT> > m_sNodes; nodes_grammar(void) :nodes_grammar::base_type(m_sNodes, "nodes") { //BOOST_SPIRIT_DEBUG_NODE(m_sNode); m_sNodes = ('(' > (+m_sNode) > ')')[ qi::_val = boost::phoenix::bind( +[](const std::vector<STRINGFW>&_r) { return vectorOfStringAndBoolFW(_r); }, qi::_1 ) ] | (+m_sNode)[ qi::_val = boost::phoenix::bind( +[](const std::vector<STRINGFW>&_r) { return vectorOfStringAndBoolFW(_r); }, qi::_1 ) ]; m_sNodes.name("one or more nodes"); BOOST_SPIRIT_DEBUG_NODE(m_sNodes); } }; } int main() { parser::nodes_grammar<const char*> sGrammar; static constexpr char ac[] = "(a 0 b1 0 1 0)"; parser::vectorOfStringAndBoolFW sV; const char *p = ac; if (!boost::spirit::qi::phrase_parse(p, ac + sizeof ac - 1, sGrammar, parser::skipper<const char*>(), sV) || p != ac + sizeof ac - 1) std::cerr << "error" << std::endl; else std::cerr << "success" << std::endl; }
内容的提问来源于stack exchange,提问作者Frank Puck
相关产品推荐
相关产品推荐

