想象一下,我们想用动态C ++类型建模C struct。即我们有一组字段,每个字段都有一个名称和一个值。该值可以是一个简单的原始类型(为了示例,我只说int
)或另一个结构,即另一组字段。
这很简单:
template <typename RecursiveVariant>
struct Field
{
std::string name;
RecursiveVariant value;
};
template <typename RecursiveVariant>
using StructFields = std::vector<Field<RecursiveVariant>>;
typedef typename boost::make_recursive_variant<
int
, StructFields<boost::recursive_variant_>
>::type FieldValue;
int main(int argc, char* argv[])
{
FieldValue fv = 2;
StructFields<FieldValue> sf;
Field<FieldValue> f{"name", 2};
sf.push_back(f);
fv = sf;
return 0;
}
它按预期工作。但是,如果我们尝试使用multi_index_container
而不是std::vector
,则无法编译。如果我将StructFields
的定义更改为:
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>
>;
编译器(来自VS 15.6.3的MSVC)将发出
binary&#39; =&#39;:没有找到哪个运算符采用类型&#39; boost :: multi_index :: multi_index_container,boost :: multi_index :: multi_index_container,... <的右手操作数/ p>
在fv = sf
行投诉。它抱怨variant& operator=(const variant& rhs)
和variant& operator=(variant&& rhs)
之间存在歧义。我不确定这些operator=
是如何参与的。有没有办法解决这个问题?
答案 0 :(得分:2)
我尝试使用更简单的索引: Live On Coliru
这没有问题。
但是,对于有序索引,FieldValue变体具有嵌套的types
类型:
boost::mpl::l_item<
mpl_::long_<2>, int,
boost::mpl::l_item<
mpl_::long_<1>,
boost::multi_index::multi_index_container<
Field<boost::variant<
boost::detail::variant::recursive_flag<int>,
boost::multi_index::multi_index_container<
Field<boost::recursive_variant_>,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<boost::multi_index::tag<mpl_::na> >,
boost::multi_index::ordered_unique<boost::multi_index::member<
Field<boost::recursive_variant_>,
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
&Field<boost::recursive_variant_>::name> > >,
std::allocator<Field<boost::recursive_variant_> > > > >,
boost::multi_index::indexed_by<boost::multi_index::sequenced<>,
boost::multi_index::ordered_unique<boost::multi_index::member<
Field<boost::recursive_variant_>, std::__cxx11::basic_string<char>,
&Field<boost::recursive_variant_>::name> > >,
std::allocator<Field<boost::variant<
boost::detail::variant::recursive_flag<int>,
boost::multi_index::multi_index_container<
Field<boost::recursive_variant_>,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<boost::multi_index::tag<mpl_::na> >,
boost::multi_index::ordered_unique<boost::multi_index::member<
Field<boost::recursive_variant_>,
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
&Field<boost::recursive_variant_>::name> > >,
std::allocator<Field<boost::recursive_variant_> > > > > > >,
boost::mpl::l_end> >
如您所见,这个错误地包含recursive_variant_
而不是正确的具体类型。
我认为这是因为Field<RecursiveVariant>
隐藏recursive_variant_
占位符¹以免正确处理。我对此不太确定。
然而,好消息是,你不一定非常需要在这里使用make_recursive_variant
。
这是我可以用来工作的东西...太长时间摆弄前向声明和包装
<强> Live On Coliru 强>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace bmi = boost::multi_index;
template <typename T>
using StructFields = bmi::multi_index_container<T, bmi::indexed_by<
bmi::sequenced<>
, bmi::ordered_unique< bmi::member<T , std::string , &T::name> >
> >;
struct Field {
std::string name;
struct Value : boost::variant<int, boost::recursive_wrapper<StructFields<Field> > > {
using base = boost::variant<int, boost::recursive_wrapper<StructFields<Field> > >;
using base::base;
using base::operator=;
};
Value value;
};
using FieldValue = Field::Value;
int main()
{
FieldValue fv = 2;
StructFields<Field> sf;
sf.push_back(Field{"name", 2});
fv = sf;
}
¹(这只是MPL参数占位符的别名)
答案 1 :(得分:2)
和@sehe一样,我怀疑这个问题与Boost.MPL魔法(特别是所谓的占位符表达的识别)有些失败有关,但不知道为什么。
FWIW,用硬类型定义替换using StructFields
位似乎解决了这个问题:
template <typename RecursiveVariant>
struct StructFields: multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>
>{};
或者更好的是,只在指数上做硬式伎俩:
template <typename RecursiveVariant>
struct StructFieldsIndices:indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>{};
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, StructFieldsIndices<RecursiveVariant>
>;
后记:好的,我想我知道发生了什么。如前所述,使用“sequenced
或random_access
之类的”更简单“索引时不会出现任何问题,因此抛出ordered_unique
会导致失败。这三个是索引说明符,声明如下:
template <typename TagList=tag<> >
struct sequenced;
template <typename TagList=tag<> >
struct random_access;
template<typename Arg1,typename Arg2=mpl::na,typename Arg3=mpl::na>
struct ordered_unique;
ordered_unique
的特点是其mpl::na
默认值:在定义StructFields<boost::recursive_variant_>
的上下文中,Boost.MPL“通过{{1”看到那些mpl::na
分层并且无法将整个类型识别为具有一个arg的占位符表达式。
Post-postscript:我现在真的想到发生了什么:-)而且它与ordered_unique
无关(事实上,mpl::na
隐藏了{{1} s在其默认的sequenced
= mpl::na
参数中并且不会引发错误)。问题与tag<>
中的tag<mp::na,...,mpl::na>
arg和处理占位符表达式时Boost.MPL无法用&Field<RecursiveVariant>::name
替换member
有关(我猜是因为这是非类型模板参数)。因此,您可以将硬类型技巧限制为&Field<FieldValue>::name
的定义,如下所示:
&Field<boost::recursive_variant_>::name