I already left a comment asking for the goal:
I'd be happy to show (several) approaches iff you would be able to describe the problem you're trying to solve. Because at this point it's not at all clear what you'd require the integrated datastructure for (I'm unable to think of algorithms that would benefit from having both the B-B, C-C, B-C edges.). Off the top of my mind 2 separate graphs, an implicit graph that conjoins them, or a single graph with optionally filtered views would do. Filtering could be done by some kind of (dynamic) type switch OR it could be done using some kind of external/intrusive index. – sehe 31 mins ago
Regardless of that, you can use boost::variant
to do exactly what the question asks (that is, probably an X/Y problem question):
Demo
Live On Wandbox
#include <boost/graph/adj_list_serialize.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/variant.hpp>
#include <fstream>
using namespace boost;
namespace Nodes {
struct Building { std::string id; };
struct Contract { std::string id; };
static inline std::ostream& operator<<(std::ostream& os, Building const& b) { return os << "Building " << b.id; }
static inline std::ostream& operator<<(std::ostream& os, Contract const& b) { return os << "Contract " << b.id; }
std::string id_of(Building const& b) { return b.id; }
std::string id_of(Contract const& c) { return c.id; }
std::string shape_of(Building const& b) { return "circle"; }
std::string shape_of(Contract const& c) { return "diamond"; }
}
using Nodes::Building;
using Nodes::Contract;
using Vertex = boost::variant<Building, Contract>;
std::string id_of(Vertex const& v) {
return boost::apply_visitor([](auto const& node) { return id_of(node); }, v);
}
std::string shape_of(Vertex const& v) {
return boost::apply_visitor([](auto const& node) { return shape_of(node); }, v);
}
typedef adjacency_list<vecS, vecS, directedS, Vertex> Graph;
int main() {
Graph g;
auto office1 = add_vertex(Building{ "office1" }, g);
auto office2 = add_vertex(Building{ "office2" }, g);
auto warehouse1 = add_vertex(Building{ "warehouse1" }, g);
auto contract1 = add_vertex(Contract{ "contract1" }, g);
auto contract2 = add_vertex(Contract{ "contract2" }, g);
add_edge(office1, office2, g);
add_edge(warehouse1, contract1, g);
add_edge(contract2, contract1, g);
{
std::ofstream dot_file("graph.dot");
dynamic_properties dp;
dp.property("node_id", boost::make_transform_value_property_map(&::id_of, boost::get(boost::vertex_bundle, g)));
dp.property("shape", boost::make_transform_value_property_map(&::shape_of, boost::get(boost::vertex_bundle, g)));
dp.property("label", boost::make_transform_value_property_map(
[](Vertex const& v) { return boost::lexical_cast<std::string>(v); },
boost::get(boost::vertex_bundle, g)));
write_graphviz_dp(dot_file, g, dp);
}
print_graph(g);
}
Prints
0 --> 1
1 -->
2 --> 3
3 -->
4 --> 3
As well as generating graph.dot
for the following graph:

The source .dot looks like this:
digraph G {
office1 [label="Building office1", shape=circle];
office2 [label="Building office2", shape=circle];
warehouse1 [label="Building warehouse1", shape=circle];
contract1 [label="Contract contract1", shape=diamond];
contract2 [label="Contract contract2", shape=diamond];
office1->office2 ;
warehouse1->contract1 ;
contract2->contract1 ;
}
And I rendered it online using https://dreampuf.github.io/GraphvizOnline/