C++ Core Guidelines: Template Definitions

Contents[Show]

Template definitions deal with guidelines that are specific to a template implementation. This means, in particular, these rules focus on how a template definition depends on its context.

 super charged engine 2770374 1280

 Here are today's rules for template definitions:

The first rule is quite special.

T.60: Minimize a template’s context dependencies

Honestly, it took me a few moments to get this rule. Let's look at the function templates sort and algo. Here is a simplified example of the guidelines.

template<typename C>
void sort(C& c)
{
    std::sort(begin(c), end(c)); // necessary and useful dependency
}

template<typename Iter>
Iter algo(Iter first, Iter last) {
    for (; first != last; ++first) {
        auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
        helper(first, x);      // potentially surprising dependency:
                               // helper is chosen based on first and x    
}

 

It would be optimal but not always manageable if a template operates only on its arguments. This holds for the function template sort but not for algo.The function template algo has dependencies to sqrt and to the function helper. In the end, the implementation of algo introduces more dependencies than the interface shows.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Stay informed about my mentoring programs.

 

 

Subscribe via E-Mail.

T.61: Do not over-parameterize members (SCARY)

If a member of a template does not depend on a template parameter it removes it from the template. A member may be a type or a method. By following this rule you may decrease the code size because the non-generic code is factored out.

The example from the guidelines is quite straightforward.

template<typename T, typename A = std::allocator{}>
    // requires Regular<T> && Allocator<A>
class List {
public:
    struct Link {   // does not depend on A
        T elem;
        T* pre;
        T* suc;
    };

    using iterator = Link*;

    iterator first() const { return head; }

    // ...
private:
    Link* head;
};

List<int> lst1;
List<int, My_allocator> lst2;

 

The type Link does not depend on the template parameter A. So I can remove it and use it in List2.

template<typename T>
struct Link {
    T elem;
    T* pre;
    T* suc;
};

template<typename T, typename A = std::allocator{}>
    // requires Regular<T> && Allocator<A>
class List2 {
public:
    using iterator = Link<T>*;

    iterator first() const { return head; }

    // ...
private:
    Link* head;
};

List<int> lst1;
List<int, My_allocator> lst2;

 

This was an easy job? Yes? No! The title of this rule writes about SCARY. What does that mean? I'm at least curious but to be honest, you can ignore the next lines.

The acronym SCARY describes assignments and initializations that are Seemingly erroneous (appearing Constrained by conflicting generic parameters), but Actually, work with the Right implementation (unconstrained bY the conflict due to minimized dependencies).

I hope you get it for the details see N2911. In order not to bore you, here is the obvious idea applied to the standard container iterator: they have no dependency on the container's, key_compare, hasher, key_equal, or allocator types.

The next rule helps to reduce code bloat.

T.62: Place non-dependent class template members in a non-templated base class

Let me say it more informal: put the functionality of the template which does not depend on the template parameters in a non-templated base class.

The guidelines present a quite obvious example.

template<typename T>
class Foo {
public:
    enum { v1, v2 };
    // ...
};

 

The enumeration is independent of the type parameter T and should, therefore, be placed in a non-templated base class.

struct Foo_base {
    enum { v1, v2 };
    // ...
};

template<typename T>
class Foo : public Foo_base {
public:
    // ...
};

 

Now, Foo_base can be used without template arguments and template instantiation.

This technique is quite interesting if you want to reduce your code size. Here is a simple class template Array.

 

// genericArray.cpp

#include <cstddef>
#include <iostream>

template <typename T, std::size_t N>
class Array{
public:
    Array()= default;
    std::size_t getSize() const{
        return N;
    }
private:
  T elem[N];
};

int main(){

    Array<int, 100> arr1;
    std::cout << "arr1.getSize(): " << arr1.getSize() << std::endl;

    Array<int, 200> arr2;
    std::cout << "arr2.getSize(): " << arr2.getSize() << std::endl;
    
}

 

If you study the class template Array, you will see that the method getSize is the same except for type parameter N. Let me refactor the code and declare a class template Array that depends on the type parameter T.

 

// genericArrayInheritance.cpp

#include <cstddef>
#include <iostream>


template<typename T>
class ArrayBase {
protected:
    ArrayBase(std::size_t n): size(n) {} 
    std::size_t getSize() const {
        return size;
    };
private:
    std::size_t size;
};

template<typename T, std::size_t n>
class Array: private ArrayBase<T>{
public:    
    Array(): ArrayBase<T>(n){}
    std::size_t getSize() const {
        return  ArrayBase<T>::getSize();
    }
private:
    T data[n]; 
};   


int main(){

    Array<int, 100> arr1;
    std::cout << "arr1.getSize(): " << arr1.getSize() << std::endl;

    Array<int, 200> arr2;
    std::cout << "arr2.getSize(): " << arr2.getSize() << std::endl;
    
}

 

Array has two template parameters for the type T and the size n but ArrayBase only one template parameter for the type T. Array derives from ArrayBase. This means ArrayBase is shared between all instantiations of Array which uses the same type T. In the concrete case, the getSize method of Array uses the getSize method of ArrayBase.

Thanks to CppInsight, I can show you the compiler-generated code.

Here is the instantiation of ArrayBase<int>:

ArrayBase

And here the instantiation for Array<int, 100> and Array<int, 200>:

Array

Array200

 

 

 

 

 

 

 

 

 

 

What's next?

Of course, there are more rules left to template definitions. So my story continues with the next post. I hope my explanation to template definitions are good enough because I know many programmers are scared of templates. To me, the ideas of templates are easy to get, but the syntax has still potential.

 

 

 

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, and Ann Shatoff.

 

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

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

 

 

Mentoring

Stay Informed about my 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

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

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 696

Yesterday 7888

Week 8584

Month 152755

All 11633909

Currently are 189 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments