What makes templates slow to compile?

So I'm currently trying to implement some more generic classes (arrays, lists, vectors, etc.) and it seems for what I want to do templates would provide the easiest path forward. I know Casey often bashes them for their slow compilation times but it got me thinking about what exactly makes them slower than #define's? What do C++ compilers need to perform for templates that causes this slowness? I read somewhere that one of the main issues with slow compilation has to do with templates being declared in multiple compilation units but if that's the case then they shouldn't be too bad in a handmade hero style project given everything is compiled into essentially one translation unit.


Edited by Jason on Reason: Initial post
Have a look at why are templates bad.
mrmixer
Have a look at why are templates bad.


That post misses the bigger problem. In my opinion the biggest culprit is linker spam. With templates many different functions are plastered into every single translation unit that includes and uses them. For a large project, as translation units are coalesced by the linker, duplicate symbols need to be churned and culled. Linking is more or less a single-threaded task, so it scales very poorly, and templates heavily exasperate this problem.
"That post misses the bigger problem. In my opinion the biggest culprit is linker spam. With templates many different functions are plastered into every single translation unit that includes and uses them. For a large project, as translation units are coalesced by the linker, duplicate symbols need to be churned and culled. Linking is more or less a single-threaded task, so it scales very poorly, and templates heavily exasperate this problem."

So if this is one of the main causes then would my original statement be accurate in terms of doing a unity build to help avoid template compilation slowness? Given the entire project is compiled as one translation unit?
lld linker has multi-threading capabilities. It is not perfect, but people are working on it.
Here's the presentation from llvm conference last year: https://www.youtube.com/watch?v=yTtWohFzS6s
Slides: https://llvm.org/devmtg/2017-10/slides/Ueyama-lld.pdf - check page 30 to see multi-threading improvements.
Unity builds will definitely solve most of the linker spam problem, but you should also watch out for unnecessary template instantiations. For a (simplified) real-world example, recently in my code I replaced something like this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct Buffer {
    uint8_t * block;
    uint8_t * head;

    template<typename TYPE>
    void write_block(TYPE * t, size_t count) {
        memcpy(head, t, count * sizeof(TYPE));
        head += count * sizeof(TYPE);
    }  
};

with something like this:
1
2
3
4
void write_block(void * mem, size_t bytes) {
    memcpy(head, mem, bytes);
    head += bytes;
}

Generating multiple equivalent versions of a template for different integer types is another common variant. This kind of mistake can be sneaky when template parameters are inferred from function arguments.