目前我正在努力应对自己应该包含的地图。但是编译时我不知道嵌套的深度。
std::map<Key, std::map<Key, std::map<Key, std::map<Key, ...>>>>
有没有办法在没有无限重复自己的情况下实现这个目标?
答案 0 :(得分:7)
自引用数据结构的金锤是指针的使用。在您的特定情况下,要实现树,您可以这样做:
template <typename Key, typename Value>
struct Node {
Value data;
std::map< Key, std::shared_ptr<Node> > child;
// ...
};
树中的每个节点都包含一个值和一组通过共享指针映射维护的子Node
。 std::map
要求(根据标准)存储的类型是完整的,但shared_ptr
只需要在创建时完成允许此数据结构的类型。普通的Node*
也可以使用,但是你必须手动管理内存。
答案 1 :(得分:1)
xml的简单实现:Node扩展了自己的列表
class Node :
public std::unordered_map< std::string, std::list< Node > >
{
public:
Node( const std::string & tag_ ) : tag( tag_ ){}
void print( const std::string & indent ) const
{
std::cout << indent << '<' << tag;
if( ! attributes.empty())
{
for( std::unordered_map< std::string, std::string >::const_iterator it2 = attributes.begin(); it2 != attributes.end(); ++it2 )
{
std::cout << ' ' << it2->first << "=\"" << it2->second << "\" ";
}
}
std::cout << '>' << std::endl;
if( ! text.empty())
{
std::cout << indent << text << std::endl;
}
for( Node::const_iterator it1 = begin(); it1 != end(); ++it1 )
{
const std::list< Node > & lst = it1->second;
for( std::list< Node >::const_iterator it2 = lst.begin(); it2 != lst.end(); ++it2 )
{
(*it2).print( indent + '\t' );
}
}
std::cout << indent << "</" << tag << '>' << std::endl;
}
std::string tag;
std::string text;
std::unordered_map< std::string, std::string > attributes;
};
int _tmain(int argc, _TCHAR* argv[])
{
Node title( "title" );
title.text = "Title of the html page";
Node head( "head" );
head["title"].push_back( title );
Node html( "html" );
html["head"].push_back( head );
Node body( "body" );
body.attributes["bgcolor"] = "#F0F0F0";
Node p1( "p" );
p1.text = "Creativity and imagination are limitless.";
body["p"].push_back( p1 );
Node p2( "p" );
p2.text = "Don't listen to the annoying guys who say your projects are dreams";
body["p"].push_back( p2 );
html["body"].push_back( body );
html.print( "" );
/* Result:
<html>
<head>
<title>
Title of the html page
</title>
</head>
<body bgcolor="#F0F0F0" >
<p>
Creativity and imagination are limitless.
</p>
<p>
Don't listen to the annoying guys who say your projects are dreams
</p>
</body>
</html>
*/
return 0;
}
由于根本没有定义虚拟方法,因此不需要虚拟析构函数。
答案 2 :(得分:1)
我认为您不能以完全类型安全的方式编写实际类型 C ++,因为实例化模板时使用的所有类型都必须是 在实例化模板时完成,以及您基本需要什么 要做的是:
typedef boost::variant<Value, std::map<Key, ExtendedValue> > ExtendedValue;
typedef std::map<Key, ExtendedValue> MyMap;
这会导致模板依赖项中的递归:为了
实例化std::map
,ExtendedValue
必须完整,但按顺序排列
要实例化ExtendedValue
,std::map
必须完整。
您应该能够使用以下方法创建类型较少的版本:
typedef std::map<Key, boost::any> MyMap;
只要您放入地图的所有内容都有Value
或MyMap
类型,就可以了
应该管用。 (我在Java中做了类似的事情,其中映射了
类型为Object
。)
或者,您可以映射到包含a的boost :: variant 指向地图的指针,而不是地图本身。即使这很困难 但是,除非您将地图包装在类中,否则请指定。你很容易 forward声明一个类,并使用指针,但你不能声明 模板的实例化,直到模板中使用的类型为止 至少已知。所以你必须写下这样的东西:
class MyMap;
typedef boost::variant<Value, MyMap*> ExtendedValue;
class MyMap : private std::map<Key, ExtendedValue>
{
public:
using ...;
};
因为你必须使用指针,所以这可能是最好的方法;您
然后可以包装您需要的成员函数以确保正确的成员
管理也是如此。 (在这种情况下,像operator[]
这样的函数会
可能必须返回一个代理,以便您可以拦截写入,并且
做分配。)
答案 3 :(得分:0)
缺少代数数据类型,递归变体可以满足您的需求:
using data_type = boost::make_recursive_variant<
Value
, std::map<Key, boost::recursive_variant_>
>::type;
请注意,这允许在运行时之前不知道'shape'(换句话说,递归深度)的值。
还有其他方法可以使用Boost.Variant声明和使用递归类型,我建议你check out the documentation找出哪种方法最适合你的情况。
(特别是使用std::map<Value, data_type>
作为顶级类型而不是data_type
本身我禁止离开,即形状只有Value
的值。但这只是为了方便std::initializer_list
的{{1}}构造函数。)