Here is one approach:
#include <vector>
template<template<typename NodeT,typename T>class EdgeT, typename T=double>
struct Node {
typedef Node<EdgeT,T> self_type;
typedef EdgeT<self_type, T> edge_type;
std::vector<edge_type> edge_out;
std::vector<edge_type> edge_in;
T data;
};
template<typename NodeT,typename T>
struct Edge {
typedef NodeT node_type;
node_type* src;
node_type* dst;
int weight;
};
template<typename NodeT, typename EdgeT=typename NodeT::edge_type>
struct graph {
typedef NodeT node_type;
typedef EdgeT edge_type;
std::vector<NodeT> nodes;
};
int main() {
typedef graph< Node<Edge> > graph_type;
graph_type my_graph;
my_graph.nodes.push_back( graph_type::node_type() );
my_graph.nodes.push_back( graph_type::node_type() );
my_graph.nodes.front().edge_out.push_back( {&my_graph.nodes[0], &my_graph.nodes[1], 1} );
my_graph.nodes.back().edge_in.push_back( {&my_graph.nodes[0], &my_graph.nodes[1], 1} );
}
for another approach, you could look at how boost::variant
handles recursive variants.
Another way to approach this would be more formal. C++ template metaprogramming is a functional language -- there are various techniques from functional programming in order to describe recursive structures without forward declaration that can be used.
I'm betting a fixed point combinator of some kind might work, but it is beyond me to figure out how. :)