Tuesday Coding Tip 06 - Explicit template instantiation

posted Originally published at medium.com 2 min read

Tuesday coding tips are super short posts about various tidbits mainly from C++, but also from other programming languages I use. You can also follow the #TuesdayCodingTips hashtag on Mastodon and Linkedin.


Templated code in C++ must be defined in the header files (or a function within the same .cpp file). This is because whenever a templated function is called, the compiler generates a new implementation using the concrete type the function was called with. If you split the definition from the declaration, you need to tell it what implementations to generate and link.

That’s what explicit template instantiation is for. It is useful when you’re writing generic code for a well-known subset of types, like string manipulation. It cannot be used for totally generic types because you need to be able to list all types the template will be used with (otherwise the user will get a linker error).

// .hpp
#pragma once

#include <string>
#include <concepts>

// Note me using constructible_from instead of is_same_v. This will
// get me into trouble
template<class T>
concept NarrowOrWideString = std::constructible_from<std::string, T>
|| std::constructible_from<std::wstring, T>;

template<NarrowOrWideString StringType>
StringType StrTransformation(StringType source);

template<NarrowOrWideString StringType>
class ClassTemplate
{
public:
    static StringType StrTransformation(StringType source);
};
// .cpp
template<NarrowOrWideString StringType>
StringType StrTransformation<StringType>(StringType source)
{
   /* some transformation */
   return source;
}

template std::string StrTransformation<std::string>(std::string);
template std::wstring StrTransformation<std::wstring>(std::wstring);


template<NarrowOrWideString StringType>
StringType ClassTemplate<StringType>::StrTransformation(StringType source)
{
   /* some transformation */
   return source;
}

template class ClassTemplate<std::string>;
template class ClassTemplate<std::wstring>;
// main.cpp
int main()
{
   {
      // This links, note the explicitly created string in parameters
      auto str = StrTransformation(std::string("aaa"));
    
      auto wstr = StrTransformation(std::wstring(L"bbb"));
    
      // ERROR: Type does not conform to concept
      // auto num = StrTransformation(10);
    
      // ERROR: Type does conform to concept,
      // but template was not instantiated for const char[4]
      // auto str2 = StrTransformation("aaa");
    
      // This works, template overload was explicitly specified
      // and the concept allows conversion of const char* to std::string
      // Note that if I used is_same_v instead of constructible_from,
      // I couldn't use const char* as an argument.
      auto str3 = StrTransformation<std::string>("aaa");
    
      // Or I can make an alias
      constexpr auto&& StrTransformationA = StrTransformation<std::string>;
      auto str4 = StrTransformationA("aaa");
   }
  
   {
      // ERROR: C++ template type deduction is not good enough
      // auto str1 = ClassTemplate::StrTransformation(std::string("aaa"));
    
      // I need to be explicit
      auto str2 = ClassTemplate<std::string>::StrTransformation("aaa");
    
      // Or make an alias
      using ClassTemplateA = ClassTemplate<std::string>;
      auto str3 = ClassTemplateA::StrTransformation("aaa");
   }
}

1 Comment

2 votes

More Posts

Tuesday Coding Tip 02 - Template with type-specific API

Jakub Neruda - Mar 10

Tuesday Coding Tip 08 — Explicit template specialization

Jakub Neruda - Apr 21

Tuesday Coding Tip 05 - Object initialization in C++

Jakub Neruda - Mar 31

Tuesday Coding Tip 10— CTest

Jakub Neruda - May 5

Tuesday Coding Tip 09 — Source location

Jakub Neruda - Apr 28
chevron_left

Related Jobs

Commenters (This Week)

2 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!