C++ Core Guidelines: Surprise included with the Specialisation of Function Templates

Contents[Show]

Today, I finish the C++ core guidelines rules to templates with a big surprise for many C++ developers. I write about the specialization of function templates.

animal 1821737 1280

Let's start simple. Here is a template specialization from a bird-eyes perspective.

Template Specialisation

Templates define the behavior of families of classes and functions. Often it is necessary that special types or non-types may be treated special. To support this use case, you fully specialize in templates. Class templates can even be partially specialized.

Here is a code snippet to get a general idea.

template <typename T, int Line, int Column>     // (1)
class Matrix;

template <typename T>                           // (2)
class Matrix<T, 3, 3>{};

template <>                                     // (3)
class Matrix<int, 3, 3>{};

 

Line 1 is the primary or general template. This template must be declared at least and has to be declared before the partially or fully specialized templates. Line 2 follows with the partial specialization. Line 3 is the full specialization. 

To better understand partially and fully specialisation, I want to present a visual explanation. Think about an n-dimensional space of template parameters. In the primary template (line 1) you can choose an arbitrary type, and two arbitrary int's. In the case of the partial specialization in line 2, you can only choose the type. This means the 3-dimensional space is reduced to a line. Fully specialization means that you have one point in a 3-dimensional space. 

What is happening when you invoke the templates?

 

Matrix<int, 3, 3> m1;          // class Matrix<int, 3, 3>

Matrix<double, 3, 3> m2;       // class Matrix<T, 3, 3> 

Matrix<std::string, 4, 3> m3;  // class Matrix<T, Line, Column> => ERROR

 

m1 uses the full specialization, m2 uses the partial specialization, and m3 the primary template which causes an error because the definition is missing.

Here are three rules which the compiler uses to get the right specialization:

  1. The compiler finds only one specialisation. The compiler uses specialisation.
  2. The compiler finds more than one specialisation. The compiler uses the most specialized one. If this process ends in more than one specialization, the compiler throws an error.
  3. The compiler finds no specialisation. It uses the primary specialisation.

Okay, I have to explain what A is a more specialized template than B means. Here is the informal definition of cppreference.com: "A accepts a subset of the types that B accepts".

After the first overview, I can dig a little bit deeper into function templates.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

Specialisation and Overloading of Function Templates

Function templates make the job of template specialization easier but also more difficult at the same time.

  • Easier, because the function template only supports full specialisation.
  • More difficult because function overloading comes into play.

From the design perspective, you can specialize a function template with template specialization or overloading.

 

// functionTemplateSpecialisation.cpp

#include <iostream>
#include <string>

template <typename T>             // (1)
std::string getTypeName(T){
    return "unknown type";
}

template <>                       // (2)
std::string getTypeName<int>(int){
    return "int";
}

std::string getTypeName(double){  // (3)
    return "double";
}

int main(){
    
    std::cout << std::endl;
    
    std::cout << "getTypeName(true): " << getTypeName(true) << std::endl;
    std::cout << "getTypeName(4711): " << getTypeName(4711) << std::endl;
    std::cout << "getTypeName(3.14): " << getTypeName(3.14) << std::endl;
    
    std::cout << std::endl;
    
}

 

Line 1 has the primary template, line 2 the full specialization for int, and line 3 the overload for double. Because I'm not interested in the values for the function or function templates, I skipped them: std::string getTypeName(double), for example. The usage of the various functions is quite comfortable. The compiler deduces the types and the correct function or function template is invoked. In the case of the function overloading the compiler prefers the function overloading to the function template when the function overloading is a perfect fit.

functionTemplateSpecialisationBut where is the big surprise I mentioned in the title of my post? Here it is.

T.144: Don’t specialize function templates

The reason for the rules is quite short: function template specialization doesn't participate in overloading. Let's see what that means. My program is based on the program snippet from Demiov/Abrahams.

// dimovAbrahams.cpp

#include <iostream>
#include <string>

// getTypeName

template<typename T>            // (1) primary template
std::string getTypeName(T){
    return "unknown";
}

template<typename T>            // (2) primary template that overloads (1)
std::string getTypeName(T*){
    return "pointer";
}

template<>                      // (3) explicit specialization of (2)
std::string getTypeName(int*){
    return "int pointer";
}

// getTypeName2

template<typename T>            // (4) primary template
std::string getTypeName2(T){
    return "unknown";
}

template<>                      // (5) explicit specialization of (4)
std::string getTypeName2(int*){
    return "int pointer";
}

template<typename T>            // (6) primary template that overloads (4)
std::string getTypeName2(T*){
    return "pointer";
}

int main(){
    
    std::cout << std::endl;
    
    int *p;
    
    std::cout << "getTypeName(p): " << getTypeName(p) << std::endl;   
    std::cout << "getTypeName2(p): " << getTypeName2(p) << std::endl; 
    
    std::cout << std::endl;
    
}

 

Admittedly, the code looks quite dull but bear with me. I defined inline (1) the primary template getTypeName. Line 2 is an overload for pointers, and line 3 is a full specialization for an int pointer. In the case of getTypeName2, I made a small variation. I put the explicit specialization (line 5) before the overload for pointers (line 6).

This reordering has surprising consequences.

dimovAbrahams

In the first case, the full specialization for the int pointer is called, and in the second case, the overload of pointers. What?  The reason for this non-intuitive behavior is that overload resolution ignores function template specialization. Overload resolution operates on primary templates and functions. In both cases, overload resolutions found both primary templates. In the first case (getTypeName), the pointer variant is the better fit, and, therefore, the explicit specialization for the int pointer was chosen. In the second variant (getTypeName2), the pointer variant was chosen, but the full specialization belongs to the primary template (line 4). Consequently, it was ignored.

What's next?

While proofreading these lines, I had an idea. Templates are good for more surprises. Therefore, I am making a short detour from the core guidelines, and I will present you with a few of them. My hope is that you will remember these lines if you encounter them. 

The future of C++ speaks templates. Therefore, it's good to know more about their language.

 

 

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up Code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Stay Informed about my Mentoring

 

Mentoring

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 1422

Yesterday 6503

Week 27679

Month 7925

All 12086134

Currently are 214 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments