Check Types with Concepts

Contents[Show]

Concepts are a powerful and elegant tool to check at compile time if a type fulfills. Thanks to static_assert, you can use concepts as a standalone feature: static_assert(Concept<T>).

 TimelineCpp20Concepts

I often have the question in my C++ class: How can I be sure that my data type is moveable? Well, you can either study the dependencies between the Big Six, or you can define and use the concept Big Six. I presented in my last post "Check Types with Concepts - The Motivation" the first part of the answer and explained the very sophisticated dependencies between the Big Six. As a reminder, here are the Big Six, including move semantics:

  • Default constructor: X()
  • Copy constructor: X(const X&)
  • Copy assignment: operator = (const X&)
  • Move constructor: X(X&&)
  • Move assignment: operator = (X&&)
  • Destructor: ~(X)

Today, I want to define and use the concept Big Six.

Before I do that, I have a short disclaimer: C++20 already supports the concepts std::semiregular and std::regular.

std::semiregular and std::regular

A semiregular type has to support the Big Six and must be swappable:

  • Default constructor: X()
  • Copy constructor: X(const X&)
  • Copy assignment: operator = (const X&)
  • Move constructor: X(X&&)
  • Move assignment: operator = (X&&)
  • Destructor: ~(X)
  • Swappable: swap(X&, X&)

Additionally, std::regular requires for a type X that it supports the concept std::semiregular and is equality comparable.

  • Default constructor: X()
  • Copy constructor: X(const X&)
  • Copy assignment: operator = (const X&)
  • Move constructor: X(X&&)
  • Move assignment: operator = (X&&)
  • Destructor: ~(X)
  • Swappable: swap(X&, Y&)
  • Equality comparable: bool operator == (const X&, const X&)

That said, there is essentially no reason to define the concept BigSix. Just use the concept std::semiregular, because you get the swappable property for free. Here is a C++11 implementation of std::swap:

template <typename T>
void swap(T& a, T& b) noexcept {
    T tmp(std::move(a));  // move constructor
    a = std::move(b);     // move assignment
    b = std::move(tmp);   // move assignment
}

 

When you invoke swap(a, b), the compiler applies move semantics to its arguments a and b. Consequentially, a type supporting the concept BigSix also supports swappable and, therefore, supports the concept std::semiregular.

Now, let me implement the concept BigSix.

The Concept BigSix

Thanks to the type traits functions, implementing the concept BigSix is a no-brainer. In the first step, I define the type traits isBigSix and in the second step, I use it directly to define the concept BigSix. Here we are:

// bigSixConcept.cpp

#include <algorithm>
#include <iostream>
#include <type_traits>

template<typename T>
struct isBigSix: std::integral_constant<bool,
                                      std::is_default_constructible<T>::value &&
                                      std::is_copy_constructible<T>::value &&
                                      std::is_copy_assignable<T>::value &&
                                      std::is_move_constructible<T>::value &&
                                      std::is_move_assignable<T>::value &&
                                      std::is_destructible<T>::value>{};


template<typename T>
concept BigSix = isBigSix<T>::value;

template <BigSix T>                                   // (1)
void swap(T& a, T& b) noexcept {
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

struct MyData{                                        // (2)
  MyData() = default;
  MyData(const MyData& ) = default;
  MyData& operator=(const MyData& m) = default;

};

int main(){

    std::cout << '\n';

    MyData a, b;
    swap(a, b);                                             // (3)

    static_assert(BigSix<MyData>, "BigSix not supported");  // (4)

    std::cout << '\n';

}

 

Now, my function swap requires, that the type parameter T supports the concept BigSix (line 1). In line 3, I invoke the function swap with arguments of type MyData. Additionally, I explicitly check in line 4 if MyData supports the concept BigSix. MyData (line 2) has a default constructor and supports copy semantics. The program can be compiled and executed.

BigSixConcept

 

Does this mean that MyData supports the concept BigSix and is, therefore, moved inside my function swap? Yes, MyData supports the concept BigSix, but no, MyData is not moved inside my function swap. Copy semantic kicks in as a fallback for move semantics.

Here is a slightly modified program.

// bigSixConceptComments.cpp

#include <algorithm>
#include <iostream>
#include <type_traits>

template<typename T>
struct isBigSix: std::integral_constant<bool,
                                      std::is_default_constructible<T>::value &&
                                      std::is_copy_constructible<T>::value &&
                                      std::is_copy_assignable<T>::value &&
                                      std::is_move_constructible<T>::value &&
                                      std::is_move_assignable<T>::value &&
                                      std::is_destructible<T>::value>{};


template<typename T>
concept BigSix = isBigSix<T>::value;

template <BigSix T>                                   
void swap(T& a, T& b) noexcept {
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

struct MyData{                                        
    MyData() = default;
    MyData(const MyData& ) {
        std::cout << "copy constructor\n";
    }
    MyData& operator=(const MyData& m) {
        std::cout << "copy assignment operator\n";
        return *this;
    }

};

int main(){

    std::cout << '\n';

    MyData a, b;
    swap(a, b);       
    
    static_assert(BigSix<MyData>, "BigSix not supported");                             

    std::cout << '\n';

}

 

I added comments to the copy constructor and copy assignment operator of MyData. Executing the program shows, that both special member functions are used:

BigSixConceptComments

By the way, this observation is already documented in cppreference.com. For example, a note about the type trait std::is_move_constructible states: "Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible."

Okay, we are back to square one. We can decide if a type supports the BigSix, but we cannot decide if a type is really moved. If you want to know if your type supports move semantics and not that copy semantics is used as a fallback for move semantics, you have to study the dependency table of my previous post: "Check Types with Concepts - The Motivation".

What's next?

In my next post, I want to continue my story with ranges. Additionally, ranges will get many improvements in C++23.

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Evangelos Denaxas, 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, Ralf Holly, Juan Dent, George Liao, Daniel Ceperley, and Jon T Hess.

 

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

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

Mentoring Program in English

Do you want to stay informed about my mentoring programs? Write to This email address is being protected from spambots. You need JavaScript enabled to view it..

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.

New

Contact Me

Modernes C++,

RainerGrimmSmall

 

Tags: concepts

My Newest E-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)

Blog archive

Source Code

Visitors

Today 797

Yesterday 6750

Week 15134

Month 235970

All 9709602

Currently are 143 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments