Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does one need templates/generics? Isn't inheritance enough?

I've heard a lot of people saying that C++ templates are very powerful. I still don't seem to understand the advantages of using them instead of using inheritance.

And as I am mainly a Java developer, thought that generics and templates were one and the same thing, but accordingly to Wikipedia:

Although C++ templates, Java generics, and .NET generics are often considered similar, generics only mimic the basic behavior of C++ templates.

I'm also wondering whether using templates where one could just have used classes isn't obfuscating the code?

like image 928
Albus Dumbledore Avatar asked Jul 25 '11 08:07

Albus Dumbledore


People also ask

Is inheritance better than templates?

Templates provide sortability in a much nicer way, since the sortee doesn't need to know that it's being sorted. Complex inheritance typically results when you work with a forced mindset that everything must be an inheritance hierarchy, which is in fact rarely appropriate.

What is the difference between generics and templates?

Generics are generic until the types are substituted for them at runtime. Templates are specialized at compile time so they are not still parameterized types at runtime. The common language runtime specifically supports generics in MSIL.

Can generic types be inherited?

Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.

Does template support generic programming?

Generic programming is a key paradigm for developing reusable software components. The inherent support for generic constructs is therefore important in programming languages. As for C++, the generic construct, templates, has been supported since the language was first released.


3 Answers

Templates and inheritance fulfill different roles, and it's fairly rare where you can choose between them. One very simple description should be that templates provide a common implementation for different interfaces, where as inheritance provides a common interface for different implementations. In their usual role, templates enforce invariants over the type system at compile time; consider some of the pre-template libraries, where a Vector could only contain Object*, and everything had to derive from Object (and you had to box things like int). Inserting an int into the vector, and trying to read a double, was a runtime error (or simply undefined behavior), rather than a compile time error.

I'd disagree with the quote from Wikipedia: technically, C++ templates and Java templates are almost unrelated. The goal of Java templates is to provide compile-time enforcement of invariants over the type system, which is also one of the important uses of C++ templates, but the mechanism used is completely unrelated, and C++ templates can be used for other purposes as well.

Finally, if you're using a template where just a simple class would do the job, you're abusing templates. Just because C++ has templates doesn't mean that you should make every class and function a template.

like image 149
James Kanze Avatar answered Sep 28 '22 11:09

James Kanze


Templates occur at compile-time. Inheritance occurs at run-time. You can catch errors with templates at compile-time that you would have to unit-test inheritance for (and then hope you don't miss them). In addition, templates are cleaner and smoother than inheritance.

Consider the simple case of, in Java, List. When you have a List which is only supposed to contain, I don't know, Customers, but if in reality it holds a bunch of Object references, you can't guarantee that it doesn't contain a bunch of Animals or DatabaseConnections, and when you get it back, you have to cast and it can throw. Generics guarantee the correct result, no casts necessary. If you write a bug trying to insert something here that doesn't belong, your compiler will throw a fit. This level of security is far above what polymorphism can offer.

In addition, templates (in C++) can accept arguments other than types, like integral types, can perform compile-time type introspection, and that sort of thing. What's possible with templates is massively greater than what's possible with inheritance, and it's much safer and faster, too.

Finally, with templates, you don't have to explicitly inherit. Do I have to inherit from Addable if I want to offer operator+? No, the template will pick it up automatically. This is more maintainable than having to explicitly specify every functionality I can inherit, as when a new library comes along, I will not have to change my code to inherit from their Addable interface. In C++, I don't have to inherit from a Callable interface to allow the use of boost::function, even if it was developed after my function object was written. Java could never do that with inheritance. How could you even develop a class that can deal with variable numbers of arguments without generics? Write a Callable1, Callable2, Callable3 interface?

Consider another simple example. Let's say I want to add two objects together, and then subtract the result with a final object. If you have an IAddable interface, how could you possibly specify that the result of the addition operation is subtractable? And subtractable with what- I sure hope that's not another paramter? You'd basically have to write a new interface for every complex use case. Templates, on the other hand, maintain their type information all the way through, so if I'm performing more than a couple of operations, the results don't lose their information.

These are basically the same arguments as static and dynamic typing, effectively, where templates are static and inheritance is dynamic. Static types are significantly faster and less error-prone than dynamic types.

like image 31
Puppy Avatar answered Sep 28 '22 12:09

Puppy


In the old days, you would have used:

 List myList = new ArrayList();
 myList.add(new String("Hello"));
 ....

 String myString = (String) myList.get(0);

If somewhere you where inserting in the list a value of a different type, you would only find when retrieving the object, without information about where the real failure (inserting a wrong type of object was).

Now, you do:

 List<String> myList = new ArrayList<String>();

and the error is thrown where the mistake is.

This already was controlled "in the old days", but it involved extending the ArrayList or creating a wrapper so it only accepted the valid classes; generics simplify it a lot.

like image 37
SJuan76 Avatar answered Sep 28 '22 12:09

SJuan76