In special scenarios
I have this data structure
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=;
};
In this case, the assignment is OK
one on{"sssss"};
two wo{22};
wo = 123;
on = std::string("vvvvvvvvvvv");
on = wo;
But when I directly assign a string to wo, it prompts an error:
wo = on;
// wo = one("sadfasdfas"); //You can't do this
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<one>, int>' for 1st argument】
I have overloaded the relevant functions, but it seems to have no effect。 Who knows why this is and tell me how to make the correct modifications
The problem is that you overloaded two::operator=
for const std::string&
and const std::string&&
, neither of which is the best match for overload resolution with (non-const) rvalue argument std::string("vvvvvvvvvvv")
. Instead, the best match comes from x3::variant::operator=(T&&)
(source) which you included with using base_type::operator=
:
template <typename T, class = non_self_t<T>>
variant& operator=(T&& rhs) BOOST_NOEXCEPT_IF((std::is_nothrow_assignable<variant_type, T&&>::value))
{
var = std::forward<T>(rhs);
return *this;
}
The line var = std::forward<T>(rhs);
results in compilation error you mentioned (var
is boost::variant<boost::spirit::x3::forward_ast<one>, int>
to which a string cannot be assigned).
To make your overloads work as intended you can just make your rvalue assignment operator accept non-const rvalue reference to make it higher priority in overload resolution than templated operator=
(and to actually make it possible to adequately implement, because you wouldn't be able to move from const std::string&&
anyway):
two& operator=(std::string&& rhs) {
return *this;
}
(godbolt)
Note that you don't need to 'break inheritance cycle' second time in one
by boost::recursive_wrapper
, it can be removed. Also, // wo = one("sadfasdfas");
can be uncommented and work as intended as well.