13

What are most important things you know about templates: hidden features, common mistakes, best and most useful practices, tips...common mistakes/oversight/assumptions

I am starting to implement most of my library/API using templates and would like to collect most common patterns, tips, etc., found in practice.

Let me formalize the question: What is the most important thing you've learned about templates?

Please try to provide examples -- it would be easier to understand, as opposed to convoluted and overly dry descriptions

Thanks

strager
  • 88,763
  • 26
  • 134
  • 176
  • 1
    This should be a community wiki. It's subjective. – strager Mar 01 '09 at 01:30
  • I don't think it's subjective. From what I have read, it follows that Sasha wants some tips on how to use templates. However, your edit could have totally changed the meaning of the original question. In that case, ignore this comment. – isekaijin Mar 01 '09 at 09:29

12 Answers12

20

From "Exceptional C++ style", Item 7: function overload resolution happens before templates specialization. Do not mix overloaded function and specializations of template functions, or you are in for a nasty surprise at which function actually gets called.

template<class T> void f(T t) { ... }   // (a)
template<class T> void f(T *t) { ... }  // (b)
template<> void f<int*>(int *t) { ... } // (c)
...
int *pi; f(pi); // (b) is called, not (c)!

On top of Item 7:

Worse yet, if you omit the type in template specialization, a different function template might get specialized depending on the order of definition and as a result a specialized function may or may not be called.

Case 1:

template<class T> void f(T t) { ... }  // (a)
template<class T> void f(T *t) { ... } // (b)
template<> void f(int *t) { ... }      // (c) - specializes (b)
...
int *pi; f(pi); // (c) is called

Case 2:

template<class T> void f(T t) { ... }  // (a)
template<> void f(int *t) { ... }      // (c) - specializes (a)
template<class T> void f(T *t) { ... } // (b)
...
int *pi; f(pi); // (b) is called
Alex B
  • 82,554
  • 44
  • 203
  • 280
7

This may not be popular, but I think it needs to be said.

Templates are complicated.

They are awesomely powerful, but use them wisely. Don't go too crazy, don't have too many template arguments... Don't have too many specializations... Remember, other programmers have to read this too.

And most of all, stay away from template metaprogramming...

dicroce
  • 45,396
  • 28
  • 101
  • 140
6

I'd have to say Coplien's Curiously Recurring Template Pattern (CRTP) is the one template trick that I find myself reaching for over & over again. Essentially it allows you to inject statically customized functionality into a derived class by inheriting from a base class that is parameterized on the derived class name. Mind boggling, but amazingly useful (some call it static polymorphism).

Also, I'll second Neil Butterworth's advice to read "C++ Templates" and throw in Alexandrescu's Modern C++ Design.

Drew Hall
  • 28,429
  • 12
  • 61
  • 81
5

Template Tip of the Day: Did you know you can specialize chosen functions of template instantiations:

#include <iostream>
#include <vector>

namespace std {
    template<>
    void vector<int>::clear() {
    std::cout << "Clearing..." << std::endl;
    resize(0);
    }
}

int main() {
    std::vector<int> v;
    v.push_back(1);
    v.clear();
}

ouputs: Clearing...

Of course, this is only an example and, as @Desmond Gold noticed, changing behaviour of standard containers may leads to undefined behaviour.

kyku
  • 5,892
  • 4
  • 43
  • 51
  • any user-defined template specializations inside `namespace std` is undefined behavior unless permitted by the standard. – Desmond Gold Jul 10 '22 at 05:53
4

This question is a bit like "I'm going to implement most of my library using functions, what are the common mistakes in using functions?" It's hard to come up with sensible answers to such questions, but here's my advice - read a good book. I recommend "C++ Templates" by Vandevoorde & Josuttis,

  • I've read parts of this book -- it's good. However, I just want to collect common tips encountered by a program. Thanks... –  Mar 01 '09 at 00:05
  • 2
    @Neil: Common mistakes in using functions include: passing parameters by value or by pointer/reference when you want the other way; returning pointers/references to local variables or temporaries; relying on the order of evaluation of arguments (which C++ does not guarantee). – j_random_hacker Mar 01 '09 at 10:11
4

One common mistake is that a template constructor or assignment operator will not suppress the compiler generated one:

template <typename T>
class A {
public:
  template <typename S>
  A(A<S> const &);

  template <typename S>
  A & operator=(A<S> const &);

private:
  int * i;
}; 

Although these functions look like the copy constructor and copy assignment operator, the compiler does not see it that way and generates the implicit versions anyway. The result is that any actions performed by these functions (eg. deep copy of a member) will not take place when the object is copied or assigned to from the same type:

void foo (A<int>);

void bar () {
  A<int> a1;
  foo (a1);   // Implicitly generated copy ctor called

  A<long> a2;
  foo (a2);   // Template ctor called.

  A<int> a3;
  a3 = a1;   // Implicitly generated copy assignment operator called

  a3 = a2;   // Template assignment operator called
}

The reason for this behaviour is due to a special rule in overload resolution (13.3.3):

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

[...]

— F1 is a non-template function and F2 is a function template specialization, or, if not that,

In the examples above, overload resolution sees two functions with the same signature, one of which is a template. The non template function (the implicitly generated copy constructor/copy assignment operator) wins and so is called.

Alex B
  • 82,554
  • 44
  • 203
  • 280
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • Another way to see it is that copy ctor and operator= are templates inside templates. When A is instantiated, copy ctor and operator= member templates still do not exist as real functions, hence implicit copy ctor and operator= are generated for A. – Alex B Mar 05 '09 at 00:03
  • 1
    To avoid this for assignment operator, you can have a non-template member that delegates to a template member: A& operator=(const A &a) { return operator=(a); } Can't see any way to do this for copy ctor, except for writing a separate non-template member. – Alex B Mar 05 '09 at 00:07
2

The STL is your friend.

Glenn
  • 7,874
  • 3
  • 29
  • 38
2

Here are some rules:

  1. Don't write any templates unless you're writing a very, very generic library (STL and Boost are two prominent examples).
  2. Don't instantiate any non-trivial template too many times. Instantiating huge template classes is especially overkill. You should consider using inheritance and polymorphism (the simple way, I mean, using virtual functions).
  3. If you're writing any templates, knowing when to use const, mutable and volatile will save users of the template both compile and execution time.
  4. If you're instantiating any templates, use a good compiler.
isekaijin
  • 19,076
  • 18
  • 85
  • 153
2

Read Meyers's Effective STL and C++ books, and Alexandrescu's Modern C++ Design.

Meyers will give you the basics on easy mistakes to make and how to avoid them. Alexandrescu introduces you to a template-based programming model that should have you asking "Is this really a good idea?" the entire book.

Dan Olson
  • 22,849
  • 4
  • 42
  • 56
2

I tend to use templates quite a lot to avoid duplication of code, and to increase safety through compile checks.

In general, it helps to think while typing about what the compiler is going to do, and how the code will be generated for each type.

Being very iterative in development and building the template complexity little by little has helped me avoiding sinking in compile error messages. Don't forget to keep a simple (or mock) instantiation of the template somewhere, otherwise you might have some nasty surprises when you instantiate a monster template for the first time.

Finally, when there is no way out, get to read these compile error messages! They might look quite scary at first, but they really are helpful. Maybe at first extracting the first one, copying it in a text editor and making it look pretty will help getting used to them, and it quickly becomes second nature to read through.

small_duck
  • 3,038
  • 20
  • 28
  • 1
    To avoid repetition of SOURCE code, you mean? Because with templates you have quite a lot of repeated MACHINE code. – isekaijin Mar 01 '09 at 09:26
1

It's important to understand separate compilation and the possibility of resulting executable size increases. If you instantiate the template with the same type in several C++ files, you will get the type reproduced multiple times, at least on some compilers.

Uri
  • 88,451
  • 51
  • 221
  • 321
  • Which compilers would those be? –  Feb 28 '09 at 23:55
  • Hmm, I have certainly instantiated a template more than in one translation unit, without causing any "blowouts", as template instantiation doesn't violate one definition rule. However, if a compiler has a poor support of templates, anything is possible, even a WWIII! –  Feb 28 '09 at 23:59
  • Modern linkers discard the duplicates. This has been so for years - it's not really a compiler issue. –  Mar 01 '09 at 00:12
  • I've seen quite a few legacy projects that use ancient compilers where this was an issue. Do most new compilers prelink to avoid this? I haven't done much C++ for quite a while, and executable size is not that much of an issue anymore. – Uri Mar 01 '09 at 00:23
0

I've used c++ and templates a lot, including more advanced template metaprogramming and my feeling is that their usefulness is overrated. They were originally added to the c++ language, well after the creation of c++, to tack on a generic programming capability. This simply allows one to focus on the logic of the code without regard for types, potentially making code clearer and reusable.

My programming philosophy is to understand the original purpose and design of the language and it's features in order to really appreciate the language. I feel template metaprogramming is bastardization of templates and should be avoided. Templates are however useful for defining higher level generic types such as the case of Tuples.