There’s this annoying thing I’ve seen this in many codebases, and I’ve wrote it myself during the years.

You have a function, no good default thing to do, but a bunch of specialization, so you end up writing something along these lines:

template<typename T> T get();

template<>
int get<int>() {
    return 1;
}

Which in normal situations works fine.

Unfortunately, when unknown specializations are used produces link errors (undefined reference in clang and gcc, or unresolved external symbol in MSVC), e.g. in this example on godbolt.

These errors are sometimes complicated to identify, because they’re reported late in the build process, and won’t have any line number associated with them. In some cases, a simple text search in your codebase (e.g. for get<short>, as in the godbolt example) won’t help, as these usages can be nested into many layers of templates, and hard to find, especially if your codebase it’s large.

Recently I had an epiphany: of course, I’ve done it the wrong way all this time!

It’s one of those things that seem obvious when you write them down, almost redundant, but I’ll tell you anyway: just delete the base case.

template<typename T> T get() = delete;

template<>
int get<int>() {
    return 1;
}

Now, if you accidentally try to use the undefined get<short>, you will get a nice compile-time error, pointing you to the right place where you’re using it, as in the fixed version on godbolt.

It’s that simple, and it still took me 14 years to get here.

=delete is indeed a C++11 feature, and this feature in particular was introduced in the standard document via a defect report (DR 941), which was merged in August 2010, just few months before the publication of C++11. Special thanks go to John Spicer for reporting the issue (and this exact use case), and the unmentioned members of the CWG for fixing this in time! :)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.