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

这是我可以在之后工作的东西...摆弄前向声明和包装器的时间太长了

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 参数占位符的别名)

与@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" 索引(例如 sequencedrandom_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::nas 并且不会引发错误)。问题与 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>>
      >
  >;