C++ Template Specialization - Syntax Note

Ben Lovy - Jan 24 '20 - - Dev Community

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 <typename T>
T myIdentityFunction(T val)
{
   return val;
}
Enter fullscreen mode Exit fullscreen mode

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

#include <iostream>

int main()
{
    int someInt = 5;
    int aCopyOfTheSameInt = myIdentityFunction(someInt);
    std::cout << aCopyOfTheSameInt << "\n";
}
Enter fullscreen mode Exit fullscreen mode

This will output 5, as expected:

$ clang++ test.cpp -o test
$ ./test 
5
Enter fullscreen mode Exit fullscreen mode

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

int myIdentityFunction(int val)
{
    return val;
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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 <typename T>
    static T myIdentityFunction(T);  // Template declaration
};
Enter fullscreen mode Exit fullscreen mode

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

#include "someModule.hpp"

template <typename T>
T idksomefunctions::myIdentityFunction(T val)
{
    return val;
}

// Any specializations live here
template int idksomefunctions::myIdentityFunction(int val);
Enter fullscreen mode Exit fullscreen mode

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 <typename T>
    static T myIdentityFunction(T val)
    {
        return val;
    }

    template static int myIdentityFunction(int val);  // right??
};
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

Okay, try the other syntax:

-  template static int myIdentityFunction(int val);
+  template <> static int myIdentityFunction(int val);
Enter fullscreen mode Exit fullscreen mode

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 <typename T>
-     static int myIdentityFunction(T val)
+     T myIdentityFunction(T val)
      {
          return val;
      }

      template <>
-     static int myIdentityFunction(int val);
+     int myIdentityFunction(int val);
  }
Enter fullscreen mode Exit fullscreen mode

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>(int)'
clang-9: error: linker command failed with exit code 1 (use -v to see invocation)
Enter fullscreen mode Exit fullscreen mode

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

      template <typename T>
      int myIdentityFunction(int val)
      {
          return val;
      }

-     template <>
+     template
      int myIdentityFunction(int val);
Enter fullscreen mode Exit fullscreen mode

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


#include <iostream>

namespace idksomefunctions
{
    template <typename T>
    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";
}
Enter fullscreen mode Exit fullscreen mode

The Recap

Inside a class, you specialize via:

template<> static int myIdentityFunction(int val);
Enter fullscreen mode Exit fullscreen mode

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

template int myIdentityFunction(int val);
Enter fullscreen mode Exit fullscreen mode

The Question

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

Photo by Ricardo Gomez Angel on Unsplash

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .