automatic

C++ Core Guidelines: Template Interfaces

This post is about template interfaces due to the C++ core guidelines: “…a critical concept”, because a template interface is “a contract between a user and an implementer – and should be carefully designed.”.

automatic

Here are the rules for today:

Let me start with the first rule T.41:

T.41: Require only essential properties in a template’s concepts

What does it mean to specify only the essential properties? The guidelines provide an example of a sort algorithm that has debug support.

template<Sortable S>
    requires Streamable<S>
void sort(S& s)  // sort sequence s
{
    if (debug) cerr << "enter sort( " << s <<  ")\n";
    // ...
    if (debug) cerr << "exit sort( " << s <<  ")\n";
}

One question remains: What is the issue if you specify non-essential properties? This means that your concepts are firmly bound to the implementation. The result may be that a slight change in the implementation changes your concepts. In the end, your interface becomes quite unstable. 

T.42: Use template aliases to simplify notation and hide implementation details

Since C++11, we have template aliases. A template alias is a name that refers to a family of types. Using them makes your code more readable and helps you to get rid of type traits. My previous post  C++ Core Guidelines: Definition of Concepts, the Second, provides more information to type traits.

Let’s see what the guidelines mean by readability. The first example uses type traits:

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    template<typename T>
    void user(T& c)
    {
        // ...
        typename container_traits<T>::value_type x; // bad, verbose
        // ...
    }
    

    Here is the corresponding case with template aliases.

    template<typename T>
    using Value_type = typename container_traits<T>::value_type;
    
    void user2(T& c)
    {
        // ...
        Value_type<T> x;
        // ...
    }
    

    Readability is also the argument that holds for the next rule

    T.43: Prefer using over typedef for defining aliases

    There are two arguments from the readability perspective to prefer using over typedef. First, using comes when used. Second, using it feels quite similar to auto. Additionally, using can easily be used for template aliases.

    typedef int (*PFI)(int);     // OK, but convoluted
    
    using PFI2 = int (*)(int);   // OK, preferred
    
    template<typename T>
    typedef int (*PFT)(T);       // error (1) 
    
    template<typename T>
    using PFT2 = int (*)(T);     // OK
    

    The first two lines define a pointer to a function (PFI and PFI2) that takes an int and returns an int. In the first case, typedef is used, and in the second line using. The last two lines define a function template (PFT2) which takes a type parameter T and returns an int.  Line (1) is not valid.

    T.44: Use function templates to deduce class template argument types (where feasible)

    The primary reason that we have too many make_ functions, such as std::make_tuple or std::make_unique is that a function template can deduce its template arguments from its function arguments. During this process, the compiler applies a few simple conversions, such as removing the outermost const/volatile qualifier and decaying C-arrays and functions to a pointer to the first element of the C-array or a pointer to the function.

    This automatic template argument deduction makes our life as a programmer much more straightforward.

    Instead of typing

    std::tuple<int, double, std::string> myTuple = {2011, 20.11, "C++11"};
    

     

    you use the factory function std::make_tuple.

    auto myTuple = std::make_tuple(2011, 20.11, "C++11");
    

     

    Sadly, automatic template type deduction is in C++ only available for function templates. Why? Constructors of class templates are a particular static function. Right! With C++17, the compiler can deduce its template arguments from its constructor arguments. Here is the way to define myTuple in C++17.

    std::tuple myTuple = {2017, 20.17, "C++17"};
    

     

    A noticeable effect of this C++17 feature is that most of the make_ functions become obsolete with C++17.

    To know the details about class template argument deduction, including the argument deduction guide, read the post Modern C++ Features – Class Template Argument Deduction from Arne Mertz.

    Teachability of C++

    I have to admit; I like this C++17 feature. As a C++ trainer, my job is it to explain this difficult stuff. The more symmetric C++ becomes, the easier for me to speak about general ideas.  Now I can say: “A template can automatically deduce its template arguments from its function arguments.”.  In the past, I had to say this works only for function templates.

    Here is a simple example:

    // templateArgumentDeduction.cpp
    
    #include <iostream>
    
    template <typename T>
    void showMe(const T& t){
      std::cout << t << std::endl;
    }
    
    template <typename T>
    struct ShowMe{
      ShowMe(const T& t){
        std::cout << t << std::endl;
      }
    };
    
    int main(){
      
      std::cout << std::endl;
        
      showMe(5.5);          // not showMe<double>(5.5);
      showMe(5);            // not showMe<int>(5);
        
      ShowMe(5.5);          // not ShowMe<double>(5.5);
      ShowMe(5);            // not ShowMe<int>(5);
      
      std::cout << std::endl;
        
    }
    

     

    The usage of the function template showMe or the class template ShowMe feels the same. From the user’s perspective, you don’t know you use a template.

    With a current GCC 8.2, the program compiles and runs.

     templateArgumentDeduction

    To be more specific, template argument deduction should work since GCC 7, Clang 5, and MSVC 19.14. cppreference.com gives you the details of the compiler support.

    What’s next?

    Do you know what a Regular or SemiRegular type is? If not, the next post to template interfaces is just the right one for you. Rule T.46 states: “Require template arguments to be at least Regular or SemiRegular.”.

     

     

     

    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, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, 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, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.

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

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

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