これは悪い考えだと確信しています。そうする正当な理由があるふりをしましょう。静的ポリモーフィズムを使用してメッセージを渡すノードのツリーがあります。重要なことに、各ノードは接続先のノードのタイプを知ることはできず、渡すメッセージのタイプを知っているだけです。ツリーをトラバースするために、CRTP を使用してビジター パターンを実装しました。これは、ツリーの最初のレベルで機能します。
ただし、ツリーの 2 番目のレイヤーにトラバースすると、次のノードのタイプは以下の AnyNode クラスを使用して消去されます。消去された型から具体的な型にダウンキャストする方法を理解できませんでした。以下の例はテストで機能しますが、おそらく非常に危険であり、たまたまメモリが配置されている場所の運だけで機能していると思います。
AnyNode::Model<T>::acceptDispatch
で完全にわかっているの訪問者の型を消去しなければならないのは問題があるようAnyNode::Concept::accept
です。しかし、コンセプトからコンセプトのモデルにダウンキャストする方法がわかりません(共変仮想cast
関数を試しましたが、うまくいきませんでした)。また、仮想メソッドをテンプレート化できないため、型指定された Visitor を仮想メソッドを使用して派生 Model クラスに渡すことはできません。
node.accept
ビジターのタイプを消去してから静的にキャストし直すことなく、ビジターを呼び出して渡す安全な方法はありますか? Model<T>
実行時にコンセプトをダウンキャストする方法はありますか? この問題にアプローチするより良い方法はありますか? おそらくSFINAEを使用して、これを解決するクレイジーな新しいC++ 11の方法はありませんか?
class AnyNode
{
struct Concept
{
virtual ~Concept() = default;
template< typename V >
void accept( V & visitor )
{
acceptDispatch( &visitor );
}
virtual void acceptDispatch( VisitorBase * ) = 0;
};
template< typename T >
struct Model : public Concept
{
Model( T &n ) : node( n ) {}
void acceptDispatch( VisitorBase * v ) override
{
// dynamic cast doesn't work, probably for good reason
NodeVisitor< T >* visitor = static_cast< NodeVisitor< T >* >( v );
std::cout << "CAST" << std::endl;
if ( visitor ) {
std::cout << "WAHOO" << std::endl;
node.accept( *visitor );
}
}
private:
T &node;
};
std::unique_ptr< Concept > mConcept;
public:
template< typename T >
AnyNode( T &node ) :
mConcept( new Model< T >( node )) {}
template< typename V >
void accept( V & visitor )
{
mConcept->accept( visitor );
}
};
編集ここにビジター基本クラスと、派生ビジターの例があります。派生した Visitor はクライアント コード (これはライブラリの一部です) によって実装されるため、基本クラスはどの Visitor が実装されるかを知ることができません。これが中心的な質問から気をそらしてしまうのではないかと思いますが、問題を少し説明するのに役立つことを願っています. ->accept( visitor )
の AnyNode ポインタで が呼び出された場合を除き、ここにあるものはすべて機能しますoutlet_visitor::operator()
。
// Base class for anything that implements accept
class Visitable
{
public:
};
// Base class for anything that implements visit
class VisitorBase
{
public:
virtual ~VisitorBase() = default;
};
// Visitor template class
template< typename... T >
class Visitor;
template< typename T >
class Visitor< T > : public VisitorBase
{
public:
virtual void visit( T & ) = 0;
};
template< typename T, typename... Ts >
class Visitor< T, Ts... > : public Visitor< Ts... >
{
public:
using Visitor< Ts... >::visit;
virtual void visit( T & ) = 0;
};
template< class ... T >
class NodeVisitor : public Visitor< T... >
{
public:
};
// Implementation of Visitable for nodes
template< class V >
class VisitableNode : public Visitable
{
template< typename T >
struct outlet_visitor
{
T &visitor;
outlet_visitor( T &v ) : visitor( v ) {}
template< typename To >
void operator()( Outlet< To > &outlet )
{
for ( auto &inlet : outlet.connections()) {
auto n = inlet.get().node();
if ( n != nullptr ) {
// this is where the AnyNode is called, and where the
// main problem is
n->accept( visitor );
}
}
}
};
public:
VisitableNode()
{
auto &_this = static_cast< V & >( *this );
_this.each_in( [&]( auto &i ) {
// This is where the AnyNode is stored on the inlet,
// so it can be retrieved by the `outlet_visitor`
i.setNode( *this );
} );
}
template< typename T >
void accept( T &visitor )
{
auto &_this = static_cast< V & >( *this );
std::cout << "VISITING " << _this.getLabel() << std::endl;
visitor.visit( _this );
// The outlets are a tuple, so we use a templated visitor which
// each_out calls on each member of the tuple using compile-time
// recursion.
outlet_visitor< T > ov( visitor );
_this.each_out( ov );
}
};
// Example instantiation of `NodeVistor< T... >`
class V : public NodeVisitor< Int_IONode, IntString_IONode > {
public:
void visit( Int_IONode &n ) {
cout << "Int_IONode " << n.getLabel() << endl;
visited.push_back( n.getLabel());
}
void visit( IntString_IONode &n ) {
cout << "IntString_IONode " << n.getLabel() << endl;
visited.push_back( n.getLabel());
}
std::vector< std::string > visited;
};