C++ Core Guidelines: Rules for the Usage of Concepts

Contents[Show]

We will get concepts with high probability in C++20. Here are the rules from the C++ core guidelines to use them.

 dice 3613500 1280

First, let me go one step back. What are concepts?

  • Concepts are a compile-time predicate. This means concepts can be evaluated at compile-time and return a boolean. 

The following questions are. What are the pros of concepts in C++? 

 

Concepts

  • Empower programmers to express their requirements as part of the interface directly.
  • Support the overloading of functions and the specialization of class templates based on the requirements of the template parameters.
  • Produce drastically improved error messages by comparing the requirements of the template parameter with the applied template arguments.
  • It can be used as placeholders for generic programming.
  • Empower you to define your concepts.

Now, one step forward. Here are the four rules for today:

Let's start with the first rule.

T.10: Specify concepts for all template arguments

There is not much to add to this rule. Because of correctness and readability, you should use concepts for all template parameters. You can do it in a verbose way.

template<typename T>
requires Integral<T>()
T gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

 

Or you can do it more concisely.

template<Integral T>
T gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

 

In the first example,  I specify the concept in the required clause, but I can use the concept Integral just in place of the keyword typename or class. The concept Integral has to be a  constant expression that returns a boolean. 

I created the concept by using std::is_integral from the type traits library.

template<typename T>
concept bool Integral(){
  return std::is_integral<T>::value;
}

 

Defining your concepts, as I did, is not the best idea.

 

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.

T.11: Whenever possible, use standard concepts

Okay, if possible, you should use the concepts from the Guidelines Support Library (GSL) or the Ranges TS. Let's see what we have. I ignore the concepts of the GSL because they are mainly part of the Ranges TS. Here are the concepts of the Range TS from the document N4569: Working Draft, C++ Extension for Ranges.

Core language concepts

  • Same
  • DerivedFrom
  • ConvertibleTo
  • Common
  • Integral
  • Signed Integral
  • Unsigned Integral
  • Assignable
  • Swappable

Comparison concepts

  • Boolean
  • EqualityComparable
  • StrictTotallyOrdered

Object concepts

  • Destructible
  • Constructible
  • DefaultConstructible
  • MoveConstructible
  • Copy Constructible
  • Movable
  • Copyable
  • Semiregular
  • Regular

Callable concepts

  • Callable
  • RegularCallable
  • Predicate
  • Relation
  • StrictWeakOrder

If you want to know what each of these concepts means, the already mentioned document N4569 gives you the answers. The concept definitions are based on the type traits library. Here are for example, the definitions of the concepts Integral, Signed Integral, and Unsigned Integral.

template <class T>
concept bool Integral() {
    return is_integral<T>::value;
}

template <class T>
concept bool SignedIntegral() {
    return Integral<T>() && is_signed<T>::value;
}

template <class T>
concept bool UnsignedIntegral() {
    return Integral<T>() && !SignedIntegral<T>();
}

 

The functions std::is_integral<T> and std::is_signed<T> are predicates from the type traits library.

Additionally, the names are used in the text of the C++ standard to define the expectations of the standard library. They are concepts that are not enforced but document the requirement for an algorithm, such as std::sort

template< class RandomIt >
void sort( RandomIt first, RandomIt last );

 

The first overload of std::sort requires two RandomAccessIterator. Now, I have to say what a RandomAccessIterator is:

  • RandomAccessIterator is a BidirectionalIterator that can be moved to point to any element in constant time.
  • BidirectionalIterator is a ForwardIterator that can be moved in both directions 
  • ForwardIterator is an Iterator that can read data from the pointed-to element.
  • The Iterator requirements describe types that can be used to identify and traverse the elements of a container.

For the details to the named requirements used in the text of the C++ standard, read cppreference.com

T.12: Prefer concept names over auto for local variables

auto is an unconstrained concept (placeholder), but you should use constrained concepts. You can use constrained concepts in each situation, where you can use unconstrained placeholders (auto). If this is not an intuitive rule?

Here is an example to make my point. 

// constrainedUnconstrainedConcepts.cpp

#include <iostream>
#include <type_traits>
#include <vector>

template<typename T>                                 // (1)
concept bool Integral(){                
  return std::is_integral<T>::value;
}

int getIntegral(int val){
  return val * 5;
}

int main(){
  
  std::cout << std::boolalpha << std::endl;
  
  std::vector<int> myVec{1, 2, 3, 4, 5};
  for (Integral& i: myVec) std::cout << i << " ";   // (2)
  std::cout << std::endl;  

  Integral b= true;                                 // (3)
  std::cout << b << std::endl;
  
  Integral integ= getIntegral(10);                  // (4)
  std::cout << integ << std::endl;
  
  auto integ1= getIntegral(10);                     // (5)
  std::cout << integ1 << std::endl;
  
  std::cout << std::endl;

}

 

I defined the concept Integral in line (1). Hence I iterate over integrals in the range-based for-loop in line (2), and the variables b and integ inline (3) and (4) have to be integrals.  I'm not so strict in line (5). Here I'm okay with an unconstrained concept.

In the end, the output of the program. 

constrainedUnconstrainedConcepts

 

T.13: Prefer the shorthand notation for simple, single-type argument concepts

The example from the C++ Core Guidelines looks quite innocent but has the potential to revolutionize the way we write templates. Here it is.

 

template<typename T>       // Correct but verbose: "The parameter is
//    requires Sortable<T>   // of type T which is the name of a type
void sort(T&);             // that is Sortable"

template<Sortable T>       // Better (assuming support for concepts): "The parameter is of type T
void sort(T&);             // which is Sortable"

void sort(Sortable&);      // Best (assuming support for concepts): "The parameter is Sortable"

 

This example shows three variations to declare the function template sort. All variations are semantically equivalent and require that the template parameter supports the concept Sortable. The last variation looks like a function declaration but is a function template declaration because the parameter is a concept, not a concrete type. To say it once more: sort becomes due to the concept parameter of a function template.   

What's next?

The C++ core guidelines say: "Defining good concepts is non-trivial. Concepts are meant to represent fundamental concepts in an application domain."  Let's see what that means in my next post.

 

 

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

Tags: Concepts

Comments   

0 #1 Jonathan OConnor 2020-06-08 17:41
Rainer,
I am trying to write a while_each_tuple_element function, and I want to ensure that the function that I pass always returns a bool for each element of the tuple. Are you planning on writing about variadic concepts?

As usual, C++ gets tricky when you combine language features in interesting ways.

For my problem, I am planning on breaking down the problem:
1. Create a concept that ensures the function returns a boolean for a given tuple element.
2. Write a concept that works on all invocations.

Regards,
Jonathan
PS: We met at Meeting C++ last November. Ich war der Trottel, der dich beim falschen Namen benannt habe. Nochmal tut es mir Leid.
Quote
0 #2 Rainer Grimm 2020-06-11 08:24
Thanks for for idea. I never thought about it but I will think about.

PS: I totally forget about the the name mistake. Wenn du wüsstest, wie oft ich schon Namen durcheinander gebracht habe.
Quote

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 1570

Yesterday 6503

Week 27827

Month 8073

All 12086282

Currently are 232 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments