void *p = /* ... */;
char *q = p;
Despite what the C++ weenies will say this is valid C! Please do not commit this cardinal sin: char *p = (char*)malloc(n);
No no no! No need to cast that! Please just write: char *p = malloc(n);
And if somebody tells you that won't compile without an explicit cast, please kindly remind them what language you are writing.[Actually I agree with you that C++ templates are really nice, chiefly for the reason that they allow you to avoid function pointers. In C++ you can pass your "callback" to a template and it gets inlined right with the body of the function.]
The compiler is of course perfectly happy when it's running in C mode. But then I'm still upset about <stdbool.h> it should get off my damn lawn.
However after playing around with the idea in a few C++ modules I was writing I came to the conclusion that it's just not worth it. Part of what makes C so enjoyable to write is its relatively "weak" typing and the flexibility that comes with that, primarily freely casting between pointer types including void*'s where necessary. Templates fight against that at every opportunity and I found myself spending more time trying to figure out how to wrangle my code into the template type system than actually making progress on the problem at hand. Newer additions like C++11 constexprs take that type-system frustration to an entirely new level.
Generics. Polymorphic functions and collections don't require the complexity of C++ templates.