Upgraded parse_tree a bit...
This commit is contained in:
parent
f1fd1f8e33
commit
ca4e470092
3 changed files with 213 additions and 61 deletions
|
|
@ -26,8 +26,11 @@ public :
|
||||||
{
|
{
|
||||||
pruneTree(_size);
|
pruneTree(_size);
|
||||||
}
|
}
|
||||||
|
eoParseTree(eoRnd<Type>& _rnd)
|
||||||
|
: EO<FType>(), parse_tree<Node>(_rnd())
|
||||||
|
{}
|
||||||
|
|
||||||
void pruneTree(unsigned _size)
|
virtual void pruneTree(unsigned _size)
|
||||||
{
|
{
|
||||||
if (_size < 1)
|
if (_size < 1)
|
||||||
return;
|
return;
|
||||||
|
|
@ -86,7 +89,6 @@ std::istream& operator>>(std::istream& is, eoParseTree<FType, Node>& eot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <class FType, class Node>
|
template <class FType, class Node>
|
||||||
class eoGpDepthInitializer : public eoRnd< eoParseTree<FType, Node>::Type >
|
class eoGpDepthInitializer : public eoRnd< eoParseTree<FType, Node>::Type >
|
||||||
{
|
{
|
||||||
|
|
@ -125,7 +127,7 @@ class eoGpDepthInitializer : public eoRnd< eoParseTree<FType, Node>::Type >
|
||||||
vector<Node>::iterator it;
|
vector<Node>::iterator it;
|
||||||
for (it = initializor.begin(); it != initializor.end(); ++it)
|
for (it = initializor.begin(); it != initializor.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->arity() > 1)
|
if (it->arity() > 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,6 +152,8 @@ class eoGpDepthInitializer : public eoRnd< eoParseTree<FType, Node>::Type >
|
||||||
what_it = initializor.begin() + last_terminal + rng.random(initializor.size() - last_terminal);
|
what_it = initializor.begin() + last_terminal + rng.random(initializor.size() - last_terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
what_it->randomize();
|
||||||
|
|
||||||
sequence.push_front(*what_it);
|
sequence.push_front(*what_it);
|
||||||
|
|
||||||
for (int i = 0; i < what_it->arity(); ++i)
|
for (int i = 0; i < what_it->arity(); ++i)
|
||||||
|
|
|
||||||
|
|
@ -75,13 +75,18 @@ template<class T>
|
||||||
class Node_alloc
|
class Node_alloc
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
Node_alloc() {};
|
|
||||||
|
|
||||||
T* allocate(void)
|
T* allocate(void)
|
||||||
{
|
{
|
||||||
T* t = static_cast<T*>(mem.allocate());
|
T* t = static_cast<T*>(mem.allocate());
|
||||||
t = new (t) T;
|
t = new (t) T;
|
||||||
//t->T(); // call constructor;
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* construct(const T& org)
|
||||||
|
{
|
||||||
|
T* t = static_cast<T*>(mem.allocate());
|
||||||
|
t = new (t) T(org);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,6 +100,7 @@ private :
|
||||||
static MemPool mem;
|
static MemPool mem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class Standard_alloc
|
class Standard_alloc
|
||||||
{
|
{
|
||||||
|
|
@ -108,6 +114,19 @@ public :
|
||||||
|
|
||||||
return new T [arity];
|
return new T [arity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T* construct(size_t arity, T* org)
|
||||||
|
{
|
||||||
|
if (arity == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
T* t new T [arity];
|
||||||
|
|
||||||
|
for (int i = 0; i < arity; ++i)
|
||||||
|
{
|
||||||
|
t = T(org[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate(T* t, size_t arity = 1)
|
void deallocate(T* t, size_t arity = 1)
|
||||||
{
|
{
|
||||||
|
|
@ -130,6 +149,11 @@ public :
|
||||||
return new T;// [arity];
|
return new T;// [arity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T* construct(const T& org)
|
||||||
|
{
|
||||||
|
return new T(org);
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate(T* t)
|
void deallocate(T* t)
|
||||||
{
|
{
|
||||||
delete t;
|
delete t;
|
||||||
|
|
@ -181,6 +205,50 @@ public :
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T* construct(size_t arity, T* org)
|
||||||
|
{
|
||||||
|
T* t;
|
||||||
|
|
||||||
|
switch(arity)
|
||||||
|
{
|
||||||
|
|
||||||
|
case 0 : return 0;
|
||||||
|
case 1 :
|
||||||
|
{
|
||||||
|
t = static_cast<T*>(mem1.allocate());
|
||||||
|
new (t) T(*org);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2 :
|
||||||
|
{
|
||||||
|
t = static_cast<T*>(mem2.allocate());
|
||||||
|
new (t) T(*org);
|
||||||
|
new (&t[1]) T(org[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3 :
|
||||||
|
{
|
||||||
|
t = static_cast<T*>(mem3.allocate());
|
||||||
|
new (t) T(*org);
|
||||||
|
new (&t[1]) T(org[1]);
|
||||||
|
new (&t[1]) T(org[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
t = new T[arity]; // does call default ctor
|
||||||
|
for (int i = 0; i < arity; ++i)
|
||||||
|
{
|
||||||
|
t[i] = T(org[i]); // constructs now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void deallocate(T* t, size_t arity)
|
void deallocate(T* t, size_t arity)
|
||||||
{
|
{
|
||||||
switch(arity)
|
switch(arity)
|
||||||
|
|
@ -221,6 +289,7 @@ private :
|
||||||
|
|
||||||
// static (non thread_safe) memory pools
|
// static (non thread_safe) memory pools
|
||||||
template <class T> MemPool Node_alloc<T>::mem = sizeof(T);
|
template <class T> MemPool Node_alloc<T>::mem = sizeof(T);
|
||||||
|
|
||||||
template <class T> MemPool Tree_alloc<T>::mem1 = sizeof(T);
|
template <class T> MemPool Tree_alloc<T>::mem1 = sizeof(T);
|
||||||
template <class T> MemPool Tree_alloc<T>::mem2 = sizeof(T) * 2;
|
template <class T> MemPool Tree_alloc<T>::mem2 = sizeof(T) * 2;
|
||||||
template <class T> MemPool Tree_alloc<T>::mem3 = sizeof(T) * 3;
|
template <class T> MemPool Tree_alloc<T>::mem3 = sizeof(T) * 3;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Parse_tree and subtree classes
|
* Parse_tree and subtree classes
|
||||||
* (c) Maarten Keijzer 1999
|
* (c) Maarten Keijzer 1999, 2000
|
||||||
|
|
||||||
* These classes may be used for educational and
|
* These classes may be used for educational and
|
||||||
* other non-commercial purposes only. Even if I
|
* other non-commercial purposes only. Even if I
|
||||||
|
|
@ -39,31 +39,39 @@
|
||||||
|
|
||||||
A parse_tree is evaluated through one of it's apply() members:
|
A parse_tree is evaluated through one of it's apply() members:
|
||||||
|
|
||||||
1) parse_tree::apply(void)
|
1) parse_tree::apply(RetVal)
|
||||||
|
|
||||||
is the simplest evaluation, it will call
|
is the simplest evaluation, it will call
|
||||||
|
|
||||||
RetVal Node::operator()(subtree<Node, RetVal>::const_iterator)
|
RetVal Node::operator()(RetVal, subtree<Node, RetVal>::const_iterator)
|
||||||
|
|
||||||
2) parse_tree::apply(It values)
|
(Unfortunately the first RetVal argument is mandatory (although you
|
||||||
|
might not need it. This is because MSVC does not support member template
|
||||||
|
functions properly. If it cannot deduce the template arguments (as is
|
||||||
|
the case in templatizing over return value) you are not allowed to
|
||||||
|
specify them. calling tree.apply<double>() would result in a syntax
|
||||||
|
error. That is why you have to call tree.apply(double()) instead.)
|
||||||
|
|
||||||
|
|
||||||
|
2) parse_tree::apply(RetVal v, It values)
|
||||||
|
|
||||||
will call:
|
will call:
|
||||||
|
|
||||||
RetVal Node::operator()(subtree<... , It values)
|
RetVal Node::operator()(RetVal, subtree<... , It values)
|
||||||
|
|
||||||
where It is whatever type you desire (most of the time
|
where It is whatever type you desire (most of the time
|
||||||
this will be a vector containing the values of your
|
this will be a vector containing the values of your
|
||||||
variables);
|
variables);
|
||||||
|
|
||||||
3) parse_tree::apply(It values, It2 moreValues)
|
3) parse_tree::apply(RetVal, It values, It2 moreValues)
|
||||||
|
|
||||||
will call:
|
will call:
|
||||||
|
|
||||||
RetVal Node::operator()(subtree<... , It values, It2 moreValues)
|
RetVal Node::operator()(RetVal, subtree<... , It values, It2 moreValues)
|
||||||
|
|
||||||
although I do not see the immediate use of this, however...
|
although I do not see the immediate use of this, however...
|
||||||
|
|
||||||
4) parse_tree::apply(It values, It2 args, It3 adfs)
|
4) parse_tree::apply(RetVal, It values, It2 args, It3 adfs)
|
||||||
|
|
||||||
that calls:
|
that calls:
|
||||||
|
|
||||||
|
|
@ -77,7 +85,7 @@
|
||||||
possible. Implement the simplest eval as:
|
possible. Implement the simplest eval as:
|
||||||
|
|
||||||
template <class It>
|
template <class It>
|
||||||
RetVal operator()(It begin) const
|
RetVal operator()(RetVal dummy, It begin) const
|
||||||
|
|
||||||
****** Internal Structure ******
|
****** Internal Structure ******
|
||||||
|
|
||||||
|
|
@ -141,6 +149,10 @@
|
||||||
tree.clear();
|
tree.clear();
|
||||||
copy(vec.begin(), vec.end(), back_inserter(tree));
|
copy(vec.begin(), vec.end(), back_inserter(tree));
|
||||||
|
|
||||||
|
or from an istream:
|
||||||
|
|
||||||
|
copy(istream_iterator<T>(my_stream), istream_iterator<T>(), back_inserter(tree));
|
||||||
|
|
||||||
Note that the back_inserter must be used as there is no
|
Note that the back_inserter must be used as there is no
|
||||||
resize member in the parse_tree. back_inserter will use
|
resize member in the parse_tree. back_inserter will use
|
||||||
the push_back member from the parse_tree
|
the push_back member from the parse_tree
|
||||||
|
|
@ -172,11 +184,16 @@ template <class T> class parse_tree
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
|
|
||||||
|
|
||||||
class subtree
|
class subtree
|
||||||
{
|
{
|
||||||
|
|
||||||
// a bit nasty way to use a pool allocator (which would otherwise use slooow new and delete)
|
/*
|
||||||
#if (defined(__GNUC__) || defined(_MSC_VER)) && !defined(_MT)
|
a bit nasty way to use a pool allocator (which would otherwise use slooow new and delete)
|
||||||
|
TODO: use the std::allocator interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if (defined(__GNUC__) || defined(_MSC_VER)) && !defined(_MT) // not multithreaded
|
||||||
Node_alloc<T> node_allocator;
|
Node_alloc<T> node_allocator;
|
||||||
Tree_alloc<subtree> tree_allocator;
|
Tree_alloc<subtree> tree_allocator;
|
||||||
#else
|
#else
|
||||||
|
|
@ -188,20 +205,28 @@ public :
|
||||||
|
|
||||||
typedef subtree* iterator;
|
typedef subtree* iterator;
|
||||||
typedef const subtree* const_iterator;
|
typedef const subtree* const_iterator;
|
||||||
//typedef std::vector<subtree >::const_reverse_iterator const_reverse_iterator;
|
|
||||||
|
|
||||||
/* Constructors, assignments */
|
/* Constructors, assignments */
|
||||||
|
|
||||||
subtree(void) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
subtree(void) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
||||||
{}
|
{}
|
||||||
subtree(const subtree& s) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
subtree(const subtree& s)
|
||||||
{ copy(s); }
|
: content(node_allocator.allocate()),
|
||||||
|
args(0),
|
||||||
|
parent(0),
|
||||||
|
_cumulative_size(1),
|
||||||
|
_depth(1),
|
||||||
|
_size(1)
|
||||||
|
{
|
||||||
|
copy(s);
|
||||||
|
}
|
||||||
|
|
||||||
subtree(const T& t) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
subtree(const T& t) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
||||||
{ copy(t); }
|
{ copy(t); }
|
||||||
|
|
||||||
template <class It>
|
template <class It>
|
||||||
subtree(It b, It e) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
subtree(It b, It e) : content(node_allocator.allocate()), args(0), parent(0), _cumulative_size(0), _depth(0), _size(1)
|
||||||
{ // initialize in prefix way for efficiency reasons
|
{ // initialize in prefix order for efficiency reasons
|
||||||
init(b, --e);
|
init(b, --e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,10 +240,12 @@ public :
|
||||||
return copy(anotherS);
|
return copy(anotherS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return copy(s);
|
copy(s);
|
||||||
|
updateAfterInsert();
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
subtree& operator=(const T& t) { return copy(t); }
|
subtree& operator=(const T& t) { copy(t); updateAfterInsert(); return *this; }
|
||||||
|
|
||||||
/* Access to the nodes */
|
/* Access to the nodes */
|
||||||
|
|
||||||
|
|
@ -253,20 +280,58 @@ public :
|
||||||
|
|
||||||
/* Evaluation with an increasing amount of user defined arguments */
|
/* Evaluation with an increasing amount of user defined arguments */
|
||||||
template <class RetVal>
|
template <class RetVal>
|
||||||
RetVal apply(RetVal v) const { return (*content)(v, begin()); }
|
void apply(RetVal& v) const { (*content)(v, begin()); }
|
||||||
|
|
||||||
template <class RetVal, class It>
|
template <class RetVal, class It>
|
||||||
RetVal apply(RetVal v, It values) const { return (*content)(v, begin(), values); }
|
void apply(RetVal& v, It values) const
|
||||||
|
{
|
||||||
|
(*content)(v, begin(), values);
|
||||||
|
}
|
||||||
|
|
||||||
template <class RetVal, class It, class It2>
|
template <class RetVal, class It>
|
||||||
RetVal apply(RetVal v, It values, It2 moreValues) const
|
void apply_mem_func(RetVal& v, It misc, void (T::* f)(RetVal&, subtree::iterator, It))
|
||||||
{ return (*content)(v, begin(), values, moreValues); }
|
{
|
||||||
|
(content->*f)(v, begin(), misc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* template <class RetVal, class It, class It2>
|
||||||
|
void apply(RetVal& v, It values, It2 moreValues) const
|
||||||
|
{ (*content)(v, begin(), values, moreValues); }
|
||||||
|
|
||||||
template <class RetVal, class It, class It2, class It3>
|
template <class RetVal, class It, class It2, class It3>
|
||||||
RetVal apply(RetVal v, It values, It2 moreValues, It3 evenMoreValues) const
|
void apply(RetVal& v, It values, It2 moreValues, It3 evenMoreValues) const
|
||||||
{ return (*content)(v, begin(), values, moreValues, evenMoreValues); }
|
{ (*content)(v, begin(), values, moreValues, evenMoreValues); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class Pred>
|
||||||
|
void find_nodes(vector<subtree*>& result, Pred& p)
|
||||||
|
{
|
||||||
|
if (p(*content))
|
||||||
|
{
|
||||||
|
result.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < arity(); ++i)
|
||||||
|
{
|
||||||
|
args[i].find_nodes(result, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Pred>
|
||||||
|
void find_nodes(vector<const subtree*>& result, Pred& p) const
|
||||||
|
{
|
||||||
|
if (p(*content))
|
||||||
|
{
|
||||||
|
result.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < arity(); ++i)
|
||||||
|
{
|
||||||
|
args[i].find_nodes(result, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Iterators */
|
/* Iterators */
|
||||||
|
|
||||||
iterator begin(void) { return args; }
|
iterator begin(void) { return args; }
|
||||||
|
|
@ -336,6 +401,8 @@ protected :
|
||||||
_cumulative_size += _size;
|
_cumulative_size += _size;
|
||||||
_depth++;
|
_depth++;
|
||||||
|
|
||||||
|
content->updateAfterInsert();
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
parent->updateAfterInsert();
|
parent->updateAfterInsert();
|
||||||
}
|
}
|
||||||
|
|
@ -382,46 +449,40 @@ private :
|
||||||
|
|
||||||
return parent->get_root();
|
return parent->get_root();
|
||||||
}
|
}
|
||||||
|
|
||||||
subtree& copy(const subtree& s)
|
subtree& copy(const subtree& s)
|
||||||
{
|
{
|
||||||
int oldArity = arity();
|
int old_arity = arity();
|
||||||
|
|
||||||
disown();
|
|
||||||
|
|
||||||
int ar = s.arity();
|
int new_arity = s.arity();
|
||||||
|
|
||||||
if (ar != oldArity)
|
if (new_arity != old_arity)
|
||||||
{
|
{
|
||||||
tree_allocator.deallocate(args, oldArity);
|
tree_allocator.deallocate(args, old_arity);
|
||||||
|
|
||||||
args = tree_allocator.allocate(ar);
|
args = tree_allocator.allocate(new_arity);
|
||||||
|
|
||||||
//if (ar > 0)
|
|
||||||
// args = new subtree [ar];
|
|
||||||
//else
|
|
||||||
// args = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(ar)
|
switch(new_arity)
|
||||||
{
|
{
|
||||||
case 3 : args[2].copy(s.args[2]); // no break!
|
case 3 : args[2].copy(s.args[2]); args[2].parent = this; // no break!
|
||||||
case 2 : args[1].copy(s.args[1]);
|
case 2 : args[1].copy(s.args[1]); args[1].parent = this;
|
||||||
case 1 : args[0].copy(s.args[0]); break;
|
case 1 : args[0].copy(s.args[0]); args[0].parent = this;
|
||||||
case 0 : break;
|
case 0 : break;
|
||||||
default :
|
default :
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ar; ++i)
|
for (int i = 0; i < new_arity; ++i)
|
||||||
{
|
{
|
||||||
args[i].copy(s.args[i]);
|
args[i].copy(s.args[i]);
|
||||||
|
args[i].parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*content = *s.content;
|
*content = *s.content;
|
||||||
|
_size = s._size;
|
||||||
adopt();
|
_depth = s._depth;
|
||||||
updateAfterInsert();
|
_cumulative_size = s._cumulative_size;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -559,20 +620,38 @@ private :
|
||||||
/* Evaluation (application), with an increasing number of user defined arguments */
|
/* Evaluation (application), with an increasing number of user defined arguments */
|
||||||
|
|
||||||
template <class RetVal>
|
template <class RetVal>
|
||||||
RetVal apply(RetVal v) const
|
void apply(RetVal& v) const
|
||||||
{ return _root.apply(v); }
|
{ _root.apply(v); }
|
||||||
|
|
||||||
template <class RetVal, class It>
|
template <class RetVal, class It>
|
||||||
RetVal apply(RetVal v, It varValues) const
|
void apply(RetVal& v, It varValues) const
|
||||||
{ return _root.apply(v, varValues); }
|
{ _root.apply(v, varValues); }
|
||||||
|
|
||||||
template <class RetVal, class It, class It2>
|
template <class RetVal, class It>
|
||||||
RetVal apply(RetVal v, It varValues, It2 moreValues) const
|
void apply_mem_func(RetVal& v, It misc, void (T::* f)(RetVal&, subtree::iterator, It))
|
||||||
{ return _root.apply(v, varValues, moreValues); }
|
{
|
||||||
|
_root.apply_mem_func(v, misc, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//template <class RetVal, class It, class It2>
|
||||||
|
// void apply(RetVal& v, It varValues, It2 moreValues) const
|
||||||
|
// { _root.apply(v, varValues, moreValues); }
|
||||||
|
|
||||||
template <class RetVal, class It, class It2, class It3>
|
//template <class RetVal, class It, class It2, class It3>
|
||||||
RetVal apply(RetVal v, It varValues, It2 moreValues, It3 evenMoreValues) const
|
// void apply(RetVal& v, It varValues, It2 moreValues, It3 evenMoreValues) const
|
||||||
{ return _root.apply(v, varValues, moreValues, evenMoreValues); }
|
// { _root.apply(v, varValues, moreValues, evenMoreValues); }
|
||||||
|
|
||||||
|
template <class Pred>
|
||||||
|
void find_nodes(vector<subtree*>& result, Pred& p)
|
||||||
|
{
|
||||||
|
_root.find_nodes(result, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Pred>
|
||||||
|
void find_nodes(vector<const subtree*>& result, Pred& p) const
|
||||||
|
{
|
||||||
|
_root.find_nodes(p);
|
||||||
|
}
|
||||||
|
|
||||||
/* Customized Swap */
|
/* Customized Swap */
|
||||||
void swap(parse_tree<T>& other)
|
void swap(parse_tree<T>& other)
|
||||||
|
|
|
||||||
Reference in a new issue