Boost Spirit X3递归Variant赋值异常:string赋值重载无效求助
问题
定义了如下递归变体数据结构:
namespace x3 = boost::spirit::x3; struct one; struct two : x3::variant<x3::forward_ast<one>,int>{ using base_type::base_type; using base_type::operator=; two& operator=(const std::string& rhs){ return *this; } two& operator=(const std::string&& rhs){ return *this; } }; struct one : x3::variant<boost::recursive_wrapper<two>,std::string>{ using base_type::base_type; using base_type::operator=; };
以下赋值操作均可正常执行:
one on{"sssss"}; two wo{22}; wo = 123; on = std::string("vvvvvvvvvvv"); on = wo;
但直接将std::string赋值给two类型的对象时会报错:
wo = on; // wo = one("sadfasdfas"); //无法执行此操作 wo = std::string("sadfasdfas");
错误信息:
/usr/local/include/boost/variant/variant.hpp:2172:14: Candidate function not viable: no known conversion from 'std::string' to 'boost::variant<boost::spirit::x3::forward_ast
, int>' for 1st argument
明明已经重载了std::string的赋值运算符,却未生效,这是什么原因?该如何正确修改?
原因分析
问题核心在于重载赋值运算符的匹配优先级冲突:
two继承自x3::variant<x3::forward_ast<one>, int>,而boost::variant本身提供了模板化的operator=,会尝试将右值转换为变体可容纳的类型。- 执行
wo = std::string("...")时,编译器会优先匹配基类的模板operator=,而非你手动重载的非模板版本。因为模板函数只要存在理论可行的转换路径(哪怕实际不可行,比如std::string无法转成int或forward_ast<one>),就会被优先考虑。 - 另外你重载的赋值运算符内部仅返回
*this,未实现实际赋值逻辑,就算匹配到也无法正确工作,但这里根本没触发匹配。
解决方案
要让自定义赋值运算符优先匹配,需阻止基类模板operator=对std::string的匹配,同时完善赋值逻辑,把std::string转换为two可容纳的类型:
修改two的定义如下:
namespace x3 = boost::spirit::x3; struct one; struct two : x3::variant<x3::forward_ast<one>,int>{ using base_type::base_type; using base_type::operator=; // 优先匹配std::string的赋值,完善赋值逻辑 two& operator=(const std::string& rhs){ *this = x3::forward_ast<one>{one{rhs}}; return *this; } two& operator=(std::string&& rhs){ *this = x3::forward_ast<one>{one{std::move(rhs)}}; return *this; } // 禁用基类模板operator=对std::string的匹配,确保自定义版本优先 template<typename T> typename std::enable_if<std::is_same_v<std::decay_t<T>, std::string>, two&>::type operator=(T&&) = delete; }; struct one : x3::variant<boost::recursive_wrapper<two>,std::string>{ using base_type::base_type; using base_type::operator=; };
关键修改说明:
- 完善赋值逻辑:将
std::string先构造为one对象,再用x3::forward_ast<one>包装,符合two的变体类型定义,完成合法赋值。 - 禁用基类模板匹配:通过
delete针对std::string的模板operator=,编译器会转而匹配你手动重载的非模板版本,确保优先级正确。
修改后,wo = std::string("sadfasdfas")可正常执行,原有合法赋值操作不受影响。
内容的提问来源于stack exchange,提问作者luohe
相关产品推荐
相关产品推荐

