
Desvantagens de uma implementação típica
O artigo intencionalmente não fornece um exemplo de uma implementação típica do padrão de visitante em C ++.
Se você não estiver familiarizado com esse padrão, poderá se familiarizar com ele aqui .
Em suma, esse padrão é muito útil se você precisar percorrer uma coleção de ponteiros para uma classe base abstrata, aplicando algum tipo de operação a eles, dependendo do tipo que está oculto por trás da abstração.
Portanto, vamos direto para as deficiências que gostaríamos de eliminar.
( ), . ( - , .)
, , . - visit ( , - ). - visit .
.
?
, - , dynamic_cast static_cast .
, .
.
.
Source:
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
(: - visit , AbstractVisitor .. T , )
. .
TypeList AbstractVisitors. AbstractVisitors , .
Source:
template< class ... T >
struct TypeList
{
};
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
template< class ...T >
struct AbstractVisitors;
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : AbstractVisitor< T >...
{
};
.. , ( , ). Dispatcher.
Source:
template< class Functor, class ... T >
struct Dispatcher;
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
- visit.
Resolver, . Dispatcher Resolver-.
, (CRTP) Dispatcher Resolver.
Source:
template< class Dispatcher, class T >
struct Resolver : AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >, Resolver< Dispatcher< Functor, TypeList< T... > >, T >...
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
.
Dispatcher. , Dispatcher , ?
, Resolver, Dispatcher .
Source:
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : virtual AbstractVisitor< T >...
{
};
template< class Dispatcher, class T >
struct Resolver : virtual AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
(AbstractObject) - (Object1, Object2), .
test test, .
:
Source:
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject
{
virtual void accept( AbstractVisitors< ObjectList >& visitor ) = 0;
};
struct Object1 : AbstractObject
{
void accept( AbstractVisitors< ObjectList >& visitor ) override
{
static_cast< AbstractVisitor< Object1 >& >( visitor ).visit( *this );
};
};
struct Object2 : AbstractObject
{
void accept( AbstractVisitors< ObjectList >& visitor ) override
{
static_cast< AbstractVisitor< Object2 >& >( visitor ).visit( *this );
};
};
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
auto l = []( auto& obj ){ test(obj); };
Dispatcher<decltype(l), ObjectList> dispatcher;
for( auto* obj : vector )
{
obj->accept( dispatcher );
}
}
(: visitor.visit( *this )
, , .)
Dispatcher - , .
, - accept AbstractObject, Object1 Object2, .. , .
Dispatchable. C - accept - . Dispatcher.
DISPATCHED, - accept Object1 Object2.
Source:
template< class TypeList >
struct Dispatchable
{
virtual ~Dispatchable() = default;
virtual void accept( AbstractVisitors< TypeList >& ) = 0;
template< class Functor >
void dispatch( Functor functor )
{
static Dispatcher< decltype(functor), TypeList > dispatcher( functor );
accept( dispatcher );
};
};
#define DISPATCHED( TYPE, TYPE_LIST ) \
void accept( AbstractVisitors< TYPE_LIST >& visitor ) override \
{ \
static_cast< AbstractVisitor< TYPE >& >( visitor ).visit( *this ); \
}
AbstractObject Dispatchable. Object1 Object2 DISPATCHED.
Source:
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject : Dispatchable< ObjectList >
{
};
struct Object1 : AbstractObject
{
DISPATCHED( Object1, ObjectList )
};
struct Object2 : AbstractObject
{
DISPATCHED( Object2, ObjectList )
};
, - accept . .
:
Source:
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
for( auto* obj : vector )
{
obj->dispatch( []( auto& obj ) { test(obj); } );
}
}
Output:
1
2
1
1
2
2
1
, , .
, .. .
Podemos aproveitar ao máximo os modelos de funções.
Ainda é possível usar a implementação usual do visitante, basta apenas fazer seu herdeiro de AbstractVisitors e passá-lo para a função de aceitação .
Quais são as desvantagens?
Indireção adicional, uma vez que Dispatcher contém um functor.
Link para o código no explorador do compilador.
Fonte completa:
#include <type_traits>
#include <iostream>
#include <vector>
template< class ... T >
struct TypeList
{
};
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
template< class ...T >
struct AbstractVisitors;
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : virtual AbstractVisitor< T >...
{
};
template< class Dispatcher, class T >
struct Resolver : virtual AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
template< class Functor, class ... T >
struct Dispatcher;
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >, Resolver< Dispatcher< Functor, TypeList< T... > >, T >...
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
template< class TypeList >
struct Dispatchable
{
virtual ~Dispatchable() = default;
virtual void accept( AbstractVisitors< TypeList >& ) = 0;
template< class Functor >
void dispatch( Functor functor )
{
static Dispatcher< decltype(functor), TypeList > dispatcher( functor );
accept( dispatcher );
};
};
#define DISPATCHED( TYPE, TYPE_LIST ) \
void accept( AbstractVisitors< TYPE_LIST >& visitor ) override \
{ \
static_cast< AbstractVisitor< TYPE >& >( visitor ).visit( *this ); \
}
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject : Dispatchable< ObjectList >
{
};
struct Object1 : AbstractObject
{
DISPATCHED( Object1, ObjectList )
};
struct Object2 : AbstractObject
{
DISPATCHED( Object2, ObjectList )
};
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
for( auto* obj : vector )
{
obj->dispatch( []( auto& obj ) { test(obj); } );
}
}