【问题标题】:Is it possible to use an STL container containing a boost::variant and use the same container type in the variant type itself?是否可以使用包含 boost::variant 的 STL 容器并在变体类型本身中使用相同的容器类型?
【发布时间】:2013-07-28 17:32:44
【问题描述】:

这似乎是不可能的,但我想我还是会问。

我已经像这样定义了boost::variant

typedef boost::variant<double, int, std::string> ConfigVariant;

稍后在我的代码中,我定义了一个std::map,如下所示:

std::map<std::string, ConfigVariant> my_map;

现在我希望能够在 my_map 中包含 std::map&lt;std::string, ConfigVariant&gt; 值。例如,我想这样做:

my_map[key1][key2] = "hello world";

我认为这是不可能的原因是因为看起来相应的变体定义看起来像这样:

typedef boost::variant<double, int, std::string, std::map<std::string, ConfigVariant> ConfigVariant;

既然做出这样的类型定义是不可能的,有什么办法可以解决这个问题吗?

【问题讨论】:

  • 是的,使用(智能)指针。
  • 也许我不明白这个问题,但my_mapmapped_typeConfigVariant,那么你怎么能把std::map&lt;std::string, ConfigVariant&gt; 放到这张地图上呢?如果您希望能够这样做,my_map 的类型必须是 std::map&lt;std::string, std::map&lt;std::string, ConfigVariant&gt;&gt;

标签: c++ boost stl


【解决方案1】:

官方文档中有一个关于recursive variant types 的部分。它解释了两种方法:使用boost::recursive_wrapperboost::make_recursive_variant。我不确定是否可以用recursive_wrapper 定义这种递归(我个人从来没有能力定义,但我远非专家)。与make_recursive_variant 相比,这真的很简单:您只需将递归变体类型替换为boost::recursive_variant_,然后使用::type 来评估元函数并获得您想要的类型。

typedef boost::make_recursive_variant<
                    double,
                    int, 
                    std::string,
                    //std::map<std::string,ConfigVariant>
                    std::map<std::string,boost::recursive_variant_>
        >::type ConfigVariant;

Running on coliru

#include <iostream>
#include <string>
#include <map>

#include <boost/variant.hpp>

typedef boost::make_recursive_variant<double, int, std::string, std::map<std::string, boost::recursive_variant_> >::type ConfigVariant;

struct printer : boost::static_visitor<>
{
    void operator()(int val) const
    {
        std::cout << val;
    }
    void operator()(double val) const
    {
        std::cout << val;
    }
    void operator()(const std::string& val) const
    {
        std::cout << val;
    }
    void operator()(const std::map<std::string,ConfigVariant>& val) const
    {
        std::cout << "map_of{ ";
        for(std::map<std::string,ConfigVariant>::const_iterator it=val.begin(),end=val.end(); it!=end; ++it)
        {
            boost::apply_visitor(*this,it->second);
            std::cout << " ";
        }
        std::cout << "}";   
    }
};


int main()
{
    ConfigVariant intconf=1;
    ConfigVariant doubleconf=1.2;
    ConfigVariant stringconf="conf";
    std::map<std::string, ConfigVariant> mapconf, mapconf2;
    mapconf["int"]=intconf;
    mapconf["string"]=stringconf;
    mapconf2["map"]=mapconf;
    mapconf2["double2"]=doubleconf;
    ConfigVariant visitable=mapconf2;

    boost::apply_visitor(printer(), visitable);
    std::cout << std::endl;
}

【讨论】:

  • 我的立场是正确的:这是variantmap 之间的选择。任你选:)
【解决方案2】:

这个问题与boost::variant 没有任何关系;您只是要求使用标准容器制作一棵 n 叉树。

答案是否定的,因为标准容器要求使用完整类型作为其模板参数。容器不能包含自身,因为正如您所观察到的,定义将是递归的。它的构造函数会假定它的构造函数已经存在。结果将是一个不完整的类型错误。

作为一种特殊情况,实际上std::vector 实现通常确实允许这样做。在 vector 的类定义完成之前,构造函数(以及任何其他需要完整元素类型的东西)实际上不会被实例化。并且所有标准容器都可以实现以使其以相同的方式工作。但这不是标准所要求的。

另见Can standard container templates be instantiated with incomplete types?;这也包含一个解决方法。为了使解决方法适用于 variant,它本身需要一个完整的类型,我建议将不完整的类型包装在 std::unique_ptr 中。

【讨论】:

    【解决方案3】:

    听起来像你想要的:

    typedef boost::variant<double, int, std::string> ConfigVariant;
    std::map<std::string, std::map<std::string, ConfigVariant> > my_map;
    

    这将允许访问表单:

    my_map["key 1"]["key 2"] = "hello world";
    

    但不是形式:

    my_map["key 1"] = "hello world";
    

    【讨论】:

      【解决方案4】:

      使用boost::any 可以让你工作。 我还在半小时前写了this code,在您的情况下也可以代替boost::variant。 它基本上只是一个美化的void* 指针,但带有类型检查断言。我猜boost::any 也只是引擎盖下的一个空洞*,但我不确定。 我忘记了我的代码不拥有数据的所有权(有意) - 如果你想使用它,你必须修改它。这可能证明很困难。 boost::any 拥有所有权,所以这可能是更好的选择。

      您的代码将是:

      typedef std::map<std::string, boost::any> ConfigMap;
      

      或者使用智能指针:

      struct Data;
      
      typedef std::map<std::string, std::unique_ptr<Data> > ConfigMap;
      
      struct Data
      {
          boost::variant<blah> value;
          ConfigMap map;
      };
      

      把它想象成一个文件夹结构。文件夹包含文件,也可以包含文件夹。

      这是在Ideone.com 编译的。不过,它会更好地包含在用户友好的类中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-24
        • 1970-01-01
        相关资源
        最近更新 更多