C++ - Graph implementation for data types other than int
Pass parameters by const &amp;

In print_list and your Graph constructor, you pass in the parameters by value.  This will make unnecessary copies of the vectors.  Passing in the values by const reference will avoid making these copies.

Make use of the auto keyword or range-based for loops

You have several places with a long type name to get the type to use for an iterator.  These would be great spots to make use of the auto keyword.  For example, auto it = l.cbegin().

However, in these cases, you can do better.  You can use the range-based for statement instead.

Combining these for the print_list function gives us

template&lt;typename T&gt;
void print_list(const std::list&lt;T&gt; &amp;l)
{
    for (auto v: l)
        std::cout &lt;&lt; v &lt;&lt; '\t';
    std::cout &lt;&lt; std::endl;
}


Similar changes can be made to your Graph constructor and print_adj_list.

You can also pass the pair to add_edge by const reference, although with the small types you're using that won't really gain much.

add_edge assumes that all your edges are bidirectional.  Some adjacency lists have edges that only go in one direction.

Do you need that cout in size?  All it is really doing is telling you that the function has been called.

You should be able to call print_list without having to specify the type.  The compiler should be able to deduce the template type to use.