C++20: Concepts - Syntactic Sugar

Contents[Show]

Today, my post is not about something new to concepts. It's about syntactic sugar. I write about abbreviated function templates. What? Abbreviated functions templates allow a sweet way to define templates.

 

TimelineCpp20Concepts

First of all, here is the definition of syntactic sugar from Wikipedia:

In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language "sweeter" for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.

The syntactic sugar I'm writing today is called abbreviated function templates.

Abbreviated Function Templates

I wrote in my last post to concepts C++20: Concepts, the Placeholder Syntax that we have since C++14 a significant asymmetry: generic lambdas introduced a new way to define function templates. Just use auto as a function parameter. On the contrary, you can not use auto as a function parameter to get a function template.

// genericLambdaFunction.cpp

#include <iostream>
#include <string>

auto addLambda = [](auto fir, auto sec){ return fir + sec; }; // (1)

auto addFunction(auto fir, auto sec){ return fir + sec; }     // (2)

int main(){

    std::cout << std::boolalpha << std::endl;

    std::cout << addLambda(1, 5) << " " << addFunction(1, 5) << std::endl;
    std::cout << addLambda(true, 5) << " " << addFunction(true, 5) << std::endl;
    std::cout << addLambda(1, 5.5) << " " << addFunction(1, 5.5) << std::endl;
    
    const std::string fir{"ge"};
    const std::string sec{"neric"};
    std::cout << addLambda(fir, sec) << " " << addFunction(fir, sec) << std::endl;

    std::cout << std::endl;

}

 

 The clang compiler speaks the clear language when I try to compile this program with the C++14 standard.

genericLambdaFunction

How strange! I can use auto as return type and for the function parameters in a lambda (line 1), but I can only use auto as the return type of a function (line 2).

I'm happy to say. This weird behaviour is gone with C++20 and the unification is, also, extended to concepts.

 

// conceptsIntegralVariationsDraft.cpp

#include <type_traits>
#include <iostream>

template<typename T>                                  // (1)
concept Integral = std::is_integral<T>::value;       

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

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

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

Integral auto gcd3(Integral auto a, Integral auto b){ // (5)
    if( b == 0 ){ return a; }
    else return gcd(b, a % b);
}

auto gcd4(auto a, auto b){                            // (6)
    if( b == 0 ){ return a; }
    return gcd(b, a % b);
}

int main(){

    std::cout << std::endl;

    std::cout << "gcd(100, 10)= "  <<  gcd(100, 10)  << std::endl;
    std::cout << "gcd1(100, 10)= " <<  gcd1(100, 10)  << std::endl;
    std::cout << "gcd2(100, 10)= " <<  gcd2(100, 10)  << std::endl;
    std::cout << "gcd3(100, 10)= " <<  gcd3(100, 10)  << std::endl;
    std::cout << "gcd4(100, 10)= " <<  gcd3(100, 10)  << std::endl; 

    std::cout << std::endl;

}

 

Let me describe in a few words the already known stuff for my previous posts to concepts. Line 1 defines the concept Integral. gcd - gcd2 (lines 2 - 4) use the concept in various ways. gcd used the required clause, gcd1 the so-called trailing requires clause, and gcd2 constrained template parameters.

With gcd3, the syntactic sugar starts. The function declaration Integral auto gcd3(Integral auto a, Integral auto b), requires from its type parameters that they support the concept Integral. This syntactic form is the new way to use a concept and to get a function template that is equivalent to the previous versions gcd - gcd2.

The syntactic form of gcd3 and gcd4 is called abbreviated function templates. Integral auto in the declaration of gcd3 is a  constrained placeholder (concept), but you can also use an unconstrained placeholder (auto)  in a function declaration such as in gcd4 (line 6). Before I make a few additional remarks to this new syntax, here is the output of the program:

conceptsIntegralVariationsDraft

Using an unconstrained placeholder (auto) in a function declaration generates a function template. The following two functions add are equivalent:

template<typename T, typename T2>
auto add(T fir, T2 sec){
    return fir + sec;
}

auto add(auto fir, auto sec){
    return fir + sec;
}

 

The key observation is, that both arguments can have different types. The same holds for concepts.

 

template<Arithmetic T, Arithmetic T2>                           // (1)
auto sub(T fir, T2 sec){
    return fir - sec;
}

Arithmetic auto sub(Arithmetic auto fir, Arithmetic auto sec){  // (2)
    return fir - sec;
}

 

The function sub requires from its arguments fir and sec,  that both support the concept Arithmetic. This means, you can invoke sub(5.5, 5), and it works with the function template (line 1) and the function (line 2). In both cases, the return type is deduced accordingly to arithmetic conversions rules. The concept Arithmetic requires that fir and sec are either integral or floating-point numbers. Here is a straightforward implementation based on the type-traits function std::is_arithmetic.

template<typename T> 
concept Arithmetic = std::is_arithmetic<T>::value; 

A Difference between Concepts TS and Concepts Draft

I explicitly mentioned that both arguments could have different types with the Concepts Draft because this is different from the previous concepts syntax, which is, supported by GCC. The previous syntax was based on the concepts TS (technical specification). In this syntax, the types of fir and sec have to be the same, and a call of the function sub(5.5, 5) would fail. Additionally, the previous syntax required no additional auto when concepts were used and the definition of concepts was slightly more verbose.

// conceptsArithmeticTS.cpp

#include <type_traits>
#include <iostream>

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

Arithmetic sub(Arithmetic fir, Arithmetic sec){
    return fir - sec;
}

int main(){

    std::cout << std::endl;

    std::cout << "sub(6, 5): " << sub(6, 5) << std::endl;      // (1)
    std::cout << "sub(5.5, 5): " << sub(5.5, 5) << std::endl;  // (2)

    std::cout << std::endl;

}

 

Because of line 2, the compilation of the program fails with the concepts TS.

conceptsArithmeticTS

Overloading

The abbreviated function templates syntax behaves as expected. The new syntax support overloading. As usual, the compiler chooses the best fitting overload.

// conceptsOverloading.cpp

#include <type_traits>
#include <iostream>

template<typename T>
concept Integral = std::is_integral<T>::value;

void overload(auto t){
    std::cout << "auto : " << t << std::endl;
}

void overload(Integral auto t){
    std::cout << "Integral : " << t << std::endl;
}

void overload(long t){
    std::cout << "long : " << t << std::endl;
}

int main(){
    
    std::cout << std::endl;

    overload(3.14);             // (1)
    overload(2010);             // (2)
    overload(2020l);            // (3)
    
    std::cout << std::endl;
    
}

 

When invoked with a double (line 1), an int (line 2), or a long int (line 3), the best fitting overload is chosen.

conceptOverloading

What's next?

Two pieces are still missing in my series of concepts and are, therefore, the topic of my next posts — the definition of concepts and the so-called template introduction, which is part of the concept TS.

 

 

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, espkk, 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, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, Tornike Porchxidze, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Satish Vangipuram, and Michael Dunsky.

 

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, and Said Mert Turkal.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

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

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 4856

Yesterday 6789

Week 48621

Month 209295

All 6857987

Currently are 182 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments