The Dark Side of C++ Templates

One of the perceived drawbacks of Java's parameterized types is “type erasure”: the compiler does not preserve parameterization information in the bytecode. C++ templates, by comparison, generate code specific to each parameterization, with “mangled” method names to ensure that you only call methods appropriate to the concrete parameteriation. For example, consider this C++ template:

template<class T> class Foo
{
    T value;
    
public:
    Foo(T val) { value = val; }
    
    T get() { return value; }
};

If you parameterize this template as Foo<int< and Foo, the compiler creates distinct code for each. You can disassemble this code on Linux using the objdump tool, and see the different names that are given to each parameterized method:

Disassembly of section .text._ZN3FooIiE3getEv:
00000000 <Foo<int>::get()>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 08                mov    0x8(%ebp),%eax
   6:   8b 00                   mov    (%eax),%eax
   8:   5d                      pop    %ebp
   9:   c3                      ret

Disassembly of section .text._ZN3FooIPiE3getEv:
00000000 <Foo<int*>::get()>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 08                mov    0x8(%ebp),%eax
   6:   8b 00                   mov    (%eax),%eax
   8:   5d                      pop    %ebp
   9:   c3                      ret

Hmm, that's the same code. Which makes sense, because on my platform a pointer is the same size as an int. However, it means that the following code compiles and runs without incident … although reversing the assignment, so that the int parameterization is treated as int*, fails rather spectacularly.

int main(int argc, char** argv)
{
    int i = 12;
    Foo<int*> f1 = Foo<int*>(&i);
    Foo<int> *f2 = (Foo<int>*)&f1;
    
    printf("%i %i\n", *(f1.get()), (*f2).get());
    return (0);
}

C++, it turns out, has its own form of type erasure, known as the C-style pointer cast. And while nobody in their right mind would use a cast like the one shown here — except maybe as a typo — the ability does exist. Because, ultimately, strong typing is a myth. Once the code comes out of the compiler, it's just pushing bytes from one place to another.

Copyright © Keith D Gregory, all rights reserved

This site does not intentionally use tracking cookies. Any cookies have been added by my hosting provider (InMotion Hosting), and I have no ability to remove them. I do, however, have access to the site's access logs, so could analyze traffic based on IP address.