multi_index_container 不适用于递归变体,因为 operator= 不明确
multi_index_container doesn't work with recursive variant because of ambiguous operator=
假设我们想要使用动态 C++ 类型对 C 结构建模。 IE。我们有一组字段,每个字段都有一个名称和一个值。该值可以是简单的原始类型(为了示例,我们只说 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)将发出
二进制“=”:未找到接受类型为“boost::multi_index::multi_index_container,boost::multi_index:: 的右手操作数的运算符multi_index_容器,...
在线投诉 fv = sf
。它抱怨 variant& operator=(const variant& rhs)
和 variant& operator=(variant&& rhs)
之间的歧义。我不确定这些 operator=
是如何参与的。有解决这个问题的方法吗?
我尝试使用更简单的索引: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_
占位符 1 而无法进行正确处理。我不太确定。
不过,好消息是,您不一定需要在此处使用 make_recursive_variant
。
这是我可以在之后工作的东西...摆弄前向声明和包装器的时间太长了
#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 参数占位符的别名)
与@sehe 一样,我怀疑问题与 Boost.MPL 魔术(特别是 so-called 占位符表达式的识别)有关,但不知何故失败。
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
>
>
>
>{};
或者,更好的是,只对索引执行 hard-type 技巧:
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>
>;
后记: 好的,我想我知道发生了什么。如前所述,使用 "simpler" 索引(例如 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 "sees" 那些 mpl::na
s 通过 ordered_unique
层,无法将整个类型识别为带有一个参数的占位符表达式。
Post-postscript: 我现在真的认为这是怎么回事 :-) 而且它与 mpl::na
无关(事实上,sequenced
在其默认的 tag<>
= tag<mp::na,...,mpl::na>
参数中隐藏了 mpl::na
s 并且不会引发错误)。问题与 member
中的 &Field<RecursiveVariant>::name
arg 以及 Boost.MPL 在处理占位符表达式时无法用 &Field<FieldValue>::name
替换 &Field<boost::recursive_variant_>::name
有关(我猜是因为这是一个 non-type 模板参数)。因此,您可以将 hard-type 技巧限制为 member
的定义,如下所示:
template <typename RecursiveVariant>
struct FieldName: member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>{};
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<FieldName<RecursiveVariant>>
>
>;
假设我们想要使用动态 C++ 类型对 C 结构建模。 IE。我们有一组字段,每个字段都有一个名称和一个值。该值可以是简单的原始类型(为了示例,我们只说 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)将发出
二进制“=”:未找到接受类型为“boost::multi_index::multi_index_container,boost::multi_index:: 的右手操作数的运算符multi_index_容器,...
在线投诉 fv = sf
。它抱怨 variant& operator=(const variant& rhs)
和 variant& operator=(variant&& rhs)
之间的歧义。我不确定这些 operator=
是如何参与的。有解决这个问题的方法吗?
我尝试使用更简单的索引: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_
占位符 1 而无法进行正确处理。我不太确定。
不过,好消息是,您不一定需要在此处使用 make_recursive_variant
。
这是我可以在之后工作的东西...摆弄前向声明和包装器的时间太长了
#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 参数占位符的别名)
与@sehe 一样,我怀疑问题与 Boost.MPL 魔术(特别是 so-called 占位符表达式的识别)有关,但不知何故失败。
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
>
>
>
>{};
或者,更好的是,只对索引执行 hard-type 技巧:
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>
>;
后记: 好的,我想我知道发生了什么。如前所述,使用 "simpler" 索引(例如 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 "sees" 那些 mpl::na
s 通过 ordered_unique
层,无法将整个类型识别为带有一个参数的占位符表达式。
Post-postscript: 我现在真的认为这是怎么回事 :-) 而且它与 mpl::na
无关(事实上,sequenced
在其默认的 tag<>
= tag<mp::na,...,mpl::na>
参数中隐藏了 mpl::na
s 并且不会引发错误)。问题与 member
中的 &Field<RecursiveVariant>::name
arg 以及 Boost.MPL 在处理占位符表达式时无法用 &Field<FieldValue>::name
替换 &Field<boost::recursive_variant_>::name
有关(我猜是因为这是一个 non-type 模板参数)。因此,您可以将 hard-type 技巧限制为 member
的定义,如下所示:
template <typename RecursiveVariant>
struct FieldName: member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>{};
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<FieldName<RecursiveVariant>>
>
>;