Metaprogramming is a pretty exotic topic. It's interesting to learn about, it's a powerful tool, and occasionally you might find it useful. But it'll never be the most often used tool in your toolbox. Sometimes, you might want your code to act on a range of unrelated types with different properties, and that's where metaprogramming comes in. With a bit of trickery, you can write an overload of a function that is only available if the argument type is integral, or if it is a pointer, or if it is either type X, Y, or Z (perhaps ignoring constness on Z).
It is essentially programming with types. Normally, your program can do things like take two numbers and produce a third number, or tell you whether a number satisfies some requirement. Metaprogramming can take two types and produce a third type, or tell you whether a type satisfies some requirement. And yes, it is probably mostly useful in library development. But then again, most code could be considered library code. You could say that everything outside your main() function is library code.
Usually, if you want to solve a problem through metaprogramming, you'll probably want to use the relevant boost libraries to do the heavy lifting. Boost.TypeTraits and of course Boost.Mpl can really simplify things for you. But it's not something you need to know, and it's not something you're likely to need very often.
Generic programming is related (and may in some cases use metaprogramming under the hood to become really generic, for example the standard library uses a touch of metaprogramming to turn raw pointers into valid iterators which is required for the "iterator" concept to be generic), but not entirely the same. And it is much more widely used.
Every time you instantiate a std::vector
, you use generic programming. Every time you use a pair of iterators to process a sequence of values, you use generic programming. Generic programming is just the idea that your code should be as generic as possible, and should work regardless of what types are put into it. A std::vector doesn't require the contained type to implement a "ICanBeContained" interface (remember how Java requires everything to be derived from Object in order for it to be stored in a container class? Which means primitive types get boxed, and that we lose type safety. That's not generic, and it's a pointless restriction.)
The code to iterate over a sequence using iterators is generic, and works with any type of iterators, or even with plain pointers.
Generic programming is very widely useful, and can often to a large extent replace OOP. (see the above example. Why would I write a container that required the contained types to implement an interface, if I can avoid that limitation?)
Often, when you use interfaces in OOP, it is not to allow the type to change during runtime (although of course that happens from time to time too), but to allow you to swap in another type at compile-time (perhaps injecting a mock object during tests, rather than using the full-fledged implementation), or just to decouple two classes. Generic programming can do that, without having you do the tedious work of defining and maintaining the interface. In those cases, generic programming means you have to write and maintain less code, and you get better performance and better type-safety. So yes, you should definitely feel at home with generic programming. C++ isn't a very good OOP language. If you want to stick strictly with OOP, you should switch to Java or another more more OOP-fixated language. C++ allows you to write OO code, but it's often not the best solution. There's a reason why almost the entire standard library relies on generic programming, rather than OOP. There is very little inheritance or polymorphism in the standard library. They didn't need it, and the code became simpler to use and more powerful without it.
And to answer your other questions, yes, Generic programming is pretty much a separate paradigm. Template metaprogramming is not. It is a fairly specific technique for manipulating the type system, and is very good at solving a small number of problems. To be considered a paradigm, I think it'd have to be much more generally useful, and approach you can use for basically everything, like functional, OO or generic programming.
I think xtofl really nailed it: Generic programming is about making your code type-unaware. (A std::vector doesn't care, or need to know what type is stored in it. It just works.)
Metaprogramming on the other hand, is about type computations. Given type T0 and T1, we can define a type T2, just like how we can, given integers N0 and N1, we can define a N2 that is the sum of N0 and N1.
The Boost.Mpl library has an obvious example of this.
In your normal code, if you have the integers N0, N1 and N2, you can create a std::vector containing these three values. I can then use some other algorithm to compute an index, and then extract the value stored at that location in the vector.
Given types T0, T1 and T2, we can create a mpl::vector containing these three types.
I can now use some other algorithm to compute an index at compile-time, and extract the type stored at that location in the vector.