由于模糊运算符=

时间:2018-03-19 20:31:51

标签: c++ boost boost-variant boost-multi-index

想象一下,我们想用动态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=是如何参与的。有没有办法解决这个问题?

2 个答案:

答案 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>
  >;

后记:好的,我想我知道发生了什么。如前所述,使用“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“通过{{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