Regular Types

The concept of a regular type goes back to the creator of the Standard Template Library (STL) Alexander Stepanov. A regular type is a user-defined type and behaves like a built-in type.


The term concrete type is strongly related to the term regular type. Let me, therefore, start this post with the term concrete type.

Concrete Types

  • A concrete type is “the simplest kind of a class” according to the C++ Core Guidelines. It is often called a value type and is not part of a type hierarchy.
  • A regular type is a type that “behaves like an int” and has, therefore, to support copy and assignment, equality, and order.

Here are more observations from the C++ Core Guidelines:

C.10: Prefer concrete types over class hierarchies

Use a concrete type if you do not have a use case for a class hierarchy. A concrete type is way easier to implement, smaller, and faster. You do not have to worry about inheritance, virtuality, references, or pointers, including memory allocation and deallocation. There is no virtual dispatch and, therefore, no run-time overhead.

To make a long story short: Apply the KISS principle (keep it simple, stupid). Your type behaves like a value.

C.11: Make concrete types regular

Regular types (ints) are easier to understand. They are per se intuitive. This means that if you have a concrete type, consider upgrading it to a regular one. The built-in types, such as int or double, are regular but so are the containers such as std::string, std::vector, or std::unordered_map.

 Finally, let me write about regular types


Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.


    Regular Types

    First of all, the concept regular types goes back to Alexander Stepanov, the father of the STL. He described his ideas about regular types in his paper “Fundamentals of Generic Programming” and refined it in his book “Elements of Programming“.

    What is the benefit of a regular type? Alexander Stepanov gives the answer in his book “Elements of Programming“:  There is a set of procedures whose inclusion in the computational basis of a type lets us place objects in data structures and use algorithms to copy objects from one data structure to another. We call types having such a basis regular, since their use guarantees regularity of behavior and, therefore, interoperability.

    To make it short, regular types behave intuitively in data structures and algorithms such as built-in types.

    What does it mean for a regular type to be intuitive? According to the paper “Fundamentals of Generic Programming“, a regular type should support the following operations:



     I assume these operations are familiar to you. In C++20, we have the concept std::regular in C++20.

    The Concept std::regular

    The concept std::regular is pretty similar to Stepanovs idea of regular types. It includes essentially move semantics:

     In C++20, we refine the idea of regular types into two concepts: std::semiregular and std::regular.


    A semiregular type X has to support the rule of six (see my last post “The Rule of Zero/Six” and has to be swappable.

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

    Only one property is left, and a semiregular type X becomes regular.


    A regular type T is regular and equality comparable:

    • Equality operator: operator == (const X&, const X&)
    • Inequality operator: operator != (const X&, const X&)

     The concept std::regular is in C++20 based on the three concepts std::movable, std::copyable, and std::semiregular.

    template<class T>
    concept movable = is_object_v<T> && move_constructible<T> &&
    assignable_from<T&, T> && swappable<T>;
    template<class T>
    concept copyable = copy_constructible<T> && movable<T> && assignable_from<T&, const T&>;
    template<class T>
    concept semiregular = copyable<T> && default_constructible<T>;
    template<class T>
    concept regular = semiregular<T> && equality_comparable<T>;


    The concept std::movable is based on the type-traits function std::s_object. Here is a possible implementation from

    template< class T>
    struct is_object : std::integral_constant<bool,
                         std::is_scalar<T>::value ||
                         std::is_array<T>::value  ||
                         std::is_union<T>::value  ||
                         std::is_class<T>::value> {};


    Furthermore, the concept std::movable is based on the concepts std::move_constructible and std::assignable_from. All other components of the concepts std::copyable and std::semiregular are also C++20 concepts.

    When you want to know more about concepts, read my post: concepts.

    You may wonder: Which type is not regular? The most prominent one is probably a reference.


    A reference is not an object and is neither regular nor semiregular.

    Let’s try it out:

    // referenceIsObject.cpp
    #include <functional>
    #include <iostream>
    #include <type_traits>
    int main() {
        std::cout << '\n';
        std::cout << std::boolalpha;
        std::cout << "std::is_object<int&>::value: " << std::is_object<int&>::value << '\n';  // (1)
        std::cout << "std::is_object<std::reference_wrapper<int>>::value: "                   // (2)
                  << std::is_object<std::reference_wrapper<int>>::value << '\n';
        std::cout << '\n';


    The output of the program shows is. A reference (line 1) is not an object:



    You may wonder about line 2 in the program referenceIsObject.cpp. A reference int& is not an object, but a reference wrapper std::reference_wrapper<int> is one. Here is the description of a reference wrapper on

    std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references. 

    This means that a vector of references std::vector<int&> is not valid, but a vector of reference wrappers std::vector<std::reference_wrapper<int>> is. Consequently, since C++11, you can have a container with reference semantics.

    // referenceSemantics.cpp
    #include <functional>
    #include <iostream>
    #include <list>
    #include <type_traits>
    #include <vector>
    int main() {
        std::cout << '\n';
        std::list<int> myList{1, 2, 3, 4, 5};
        std::vector<std::reference_wrapper<int>> myRefVector(myList.begin(), myList.end());
        for (auto l: myList) std::cout << l << " ";
        std::cout << "\n\n";
        for (auto& v: myRefVector) v *= v;             // (1)
        for (auto l: myList) std::cout << l << " ";    // (2)
        std::cout << "\n\n";


     Modifying the elements of a std::vector<std::reference_wrapper<int>> (line 1) also modifies the referenced objects (line 2).


    Let’s go one step back. What happens in C++20 when you have an algorithm that requires a regular type but uses a reference?

    // needRegularType.cpp 
    #include <concepts>
    template <std::regular T>
    class MyClass{};
    int main() {
        MyClass<int> myClass1;
        MyClass<int&> myClass2;


    I compiled the program with the latest GCC 12.2, the latest clang 15.0.0, and the latest MSVC v19 compiler. The Microsoft compiler provided the best error message:


    What’s Next?

     A value object is a small object whose equality is based on the statem but not on its identity. They will be the topic of 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, 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 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS


    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    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++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    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 *