5

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

我已经定义了一个boost::variant这样的:

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

稍后在我的代码中,我定义了一个std::map这样的:

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

现在我希望能够std::map<std::string, ConfigVariant>my_map. 例如,我想这样做:

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

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

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

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

4

4 回答 4

7

官方文档有一个关于递归变体类型的部分。它解释了两种方法:使用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;

在大肠杆菌上运行

#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;
}
于 2013-07-29T05:03:16.587 回答
4

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

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

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

另请参阅标准容器模板可以用不完整的类型实例化吗?; 这也包含一个解决方法。为了使解决方法适用于variant本身需要完整类型的 ,我建议将不完整类型包装在std::unique_ptr.

于 2013-07-29T03:13:25.223 回答
0

听起来你想要:

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";
于 2013-07-29T03:13:34.870 回答
0

使用boost::any可以使你工作。 我也在半小时前写了这段代码,它也可以在你的情况下代替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上编译的。不过,它会更好地包裹在一个用户友好的类中。

于 2013-07-29T03:55:01.930 回答