Concepts: The Next Major Change Coming to C++

The upcoming C++ standard is set to promise many different features and changes that will simplify and expand the way we write and think of code. Upon the wide variety of additions to the language, the inclusion of Concepts tops the list as the most significant one, as they are essential to simplify the use of generic programming. The currently ongoing CppCon has sparked my interest on the topic thanks to the talk given by Bjarne Stroustrup on the future of generic programming. Which covered the importance of concepts in the future of the language as described by the Concepts TS.

What is a Concept?

A Concept is a set of requirements that yield a boolean value at compile-time. They describe the characteristics needed for a certain group of objects to be used inside a template e.g.
Number: - Must provide arithmetic operators(+, +=, -, -=, *, *=, /, /=)
- Must be constructible from 0
- Must be copyable
- Must be movable

Why do we need Concepts?

- Simplify the way we write generic code by removing the verbosity and repetitiveness that templates bring e.g.

//Old C++ template version
template<typename T>
void sort(T& container)
{
    //Sort algorithm
}

//New C++ concept version
void sort(Sortable& container) //Sortable is a concept
{
    //Sort algorithm
}

- Provide more precise and helpful error messages which in the past have been known to daunting and irrelevant e.g.

int a = 42;

//Old error message 
sort(a) // -> error: 'a' doesn't have a [] operator

//New error message with concepts
sort(a) // -> error: 'a' is not Sortable (int doesn't provide [] operator)

- Allow the use of overloads for functions using concepts e. g.

template<RandomAccessIterator Iter>  //RandomAccessIterator is a concept
void distance(Iter first, Iter last)
{
  //Distance Implemnetation for random access iterators
}

template<Iterator Iter> //Iterator is a concept
void distance(Iter first, Iter last)
{
  //Distance implementation for the rest of iterators
}

- Remove the ugliness and complexity coming from the use of enable_if by replacing it with the use of the keyword requires e.g.

//Old code using enable_if
template<typename T>
class Foo
{
  public:
  template<typename U, std::enable_if<std::is_same<T, U>::value && std::is_copy_constructible<U>::value>>
  explicit Foo(const T& object) :
      object(object)
  {}

  private:
  T object;
};

//New code using concepts
template<typename T>
class Foo
{
  public:
  explicit Foo(const T& object)
  requires std::is_copy_constructible<T>::value :
  object(object)
  {}

  private:
  T object;
};


How do we use Concepts?

The first step is to either define a new concept or use an already existing one. Here is an example using the previously mentioned concept of a number:

template<typename T>
concept Number = requires(T a, T b)
{
  { a + b } -> T;
  { a += b } -> T&;
  { a - b } -> T;
  { a -= b } -> T&;
  { a * b } -> T;
  { a *= b } -> T&;
  { a / b } -> T;
  { a /= b } -> T&;
  
  { T{0} };
  
  requires std::is_copy_constructible<T>;
  requires std::is_move_constructible<T>;
}

Then we simply use the already defined concept in a function:

template<Number N>
N pow(N number)
{
  //power exponent implementation
}

Conclusion

Concepts fill in a void that was missing in the language; they effectively improve and simplify the use of generic code. It’s important to understand and appreciate their benefits to start adhering them into our code base as soon as C++20 arrives.