C++ Template Specialization - Syntax Note

cover_image
cover_image
#beginners, #cpp, #devjournal, #todayilearned

I sat down yesterday with @codemouse92 via Visual Studio Live Share for VS Code – which is an awesome tool - to do a mostly straightforward re-arrangement of some C++. Unexpectedly, we ran into something, well…unexpected. To DEV!

The Concept

In C++, you can write a template:

template T myIdentityFunction(T val) { return val; }

This nearly useless function just returns whatever is passed in, no matter the type. You use it with a concrete type, like this:

#include int main() { int someInt = 5; int aCopyOfTheSameInt = myIdentityFunction(someInt); std::cout << aCopyOfTheSameInt << "\n"; }

This will output 5, as expected:

$ clang++ test.cpp -o test $ ./test 5

When it gets used, the compiler will generate the specialized version and insert that in your binary:

int myIdentityFunction(int val) { return val; }

As I just learned today, you can specialize what types end up in your templates to retain control over what the compiler will guess:

template int myIdentityFunction(int val);

This is a silly example, but this ability lets you do things like partially specialize a template<typename T, typename U> to template<int, typename U>, and also makes implicit behaviour explicit, giving you back the keys from the compiler. You just define one for each type you need.

The Context

I don’t actually know if the fact we’re doing this in headers is relevant or not, but I’m including it for completeness in case somebody does know and wants to elaborate on this. I think the issue is just “in a class” vs. “not in a class”.

We were refactoring a library to be header-only. In a standard library, you have your declarations in someModule.hpp:

class idksomefunctions { public: idksomefunctions() = delete; // specify there should be no constructor template static T myIdentityFunction(T); // Template declaration };

And then a corresponding someModule.cpp with the actual implementations and specializations:

#include "someModule.hpp" template T idksomefunctions::myIdentityFunction(T val) { return val; } // Any specializations live here template int idksomefunctions::myIdentityFunction(int val);

To refactor this into a header, you just combine them both in someModule.hpp:

class idksomefunctions { public: idksomefunctions() = delete; // specify there should be no constructor template static T myIdentityFunction(T val) { return val; } template static int myIdentityFunction(int val); // right?? };

Not quite:

$ clang++ test.cpp -o test In file included from test.cpp:5: ./test.hpp:15:14: error: expected '<' after 'template' template static int myIdentityFunction(int val); // right?? ^ 1 error generated.

Okay, try the other syntax:

- template static int myIdentityFunction(int val); + template <> static int myIdentityFunction(int val);

Good to go!

The Switcheroo

Now, idksomefunctions doesn’t really need to be a class - it’s just, I don’t know, some functions. This could just be a namespace. No more constructor thing, no more static or public (or storage class errors), just some good ol’ functions:

- class idksomefunctions + namespace idksomefunctions { - public: - idksomefunctions() = delete; // specify there should be no constructor template - static int myIdentityFunction(T val) + T myIdentityFunction(T val) { return val; } template <> - static int myIdentityFunction(int val); + int myIdentityFunction(int val); }

Great! But wait:

$ clang++ test.cpp -o test /bin/x86_64-unknown-linux-gnu-ld: /tmp/test-d48bb0.o: in function `main': test.cpp:(.text+0x13): undefined reference to `int idksomefunctions::myIdentityFunction(int)' clang-9: error: linker command failed with exit code 1 (use -v to see invocation)

That’s no good. There’s one more change to make:

template int myIdentityFunction(int val) { return val; } - template <> + template int myIdentityFunction(int val);

Gotta take out the <> thingy, back to where we started! Now it’ll compile:

#include namespace idksomefunctions { template int myIdentityFunction(T val) { return val; } template int myIdentityFunction(int val); }; int main() { int someInt = 5; int aCopyOfTheSameInt = idksomefunctions::myIdentityFunction(someInt); std::cout << aCopyOfTheSameInt << "\n"; }
The Recap

Inside a class, you specialize via:

template<> static int myIdentityFunction(int val);

Outside of a class, though, you omit the thingy:

template int myIdentityFunction(int val);
The Question

What am I saying when I say template <> versus template here?

Photo by Ricardo Gomez Angel on Unsplash