SlideShare a Scribd company logo
concepts (since C++20)
Create "interfaces" for your templates
platis.solutions
©
for
GRCPP
Meetup
About me
Grew up in Rodos, Greece
Software Engineering at GU & Chalmers
Working with embedded systems
Teaching
DIT113, DAT265, Thesis supervision
C++, Coursera, Udemy
Open source projects
https://platis.solutions
https://github.com/platisd
Email: dimitris@platis.solutions
platis.solutions
©
for
GRCPP
Meetup
Requirements on template arguments
C++20 introduces constraints
Specify requirements on template arguments
Seamless selection of the appropriate overload or specialization
Named sets of such requirements are called concepts
Constraints and concepts
requires expression
platis.solutions
©
for
GRCPP
Meetup
Why do we need concepts?
template <typename Camera>
class AutonomousCar {
Camera mCamera;
public:
// ... A lot of code
};
How do we ensure that Camera has all required functions?
Normally, we read the Camera interface, with templates we can't.
platis.solutions
©
for
GRCPP
Meetup
Why do we need concepts?
template <typename T>
T getMedianNumber(std::vector<T> values) {
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
What kind of types does it make sense for getMedian accept?
What kind of types can getMedian accept?
platis.solutions
©
for
GRCPP
Meetup
Let's make our getMedianNumber more explicit
template<typename T>
T getMedianNumber(std::vector<T> values) {
static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"T must be an integral or floating-point");
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
static_assert is great, but can make things more readable?
platis.solutions
©
for
GRCPP
Meetup
Let's make our own constraint
template<typename T>
requires std::integral<T> || std::floating_point<T>
// requires std::is_integral_v<T> || std::is_floating_point_v<T>
T getMedianNumber(std::vector<T> values) {
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
std::vector<std::string> files{"file22.txt", "file11.txt", "file33.txt"};
std::cout << getMedianNumber(files) << std::endl; // Compilation error
std::vector numbers{0, 9, 5, 7, 3, 6, 2, 8, 1, 4, 10};
std::cout << getMedianNumber(numbers) << std::endl; // 5
"No operand of the disjunction is satisfied"
requires std::integral<T> || std::floating_point<T>
platis.solutions
©
for
GRCPP
Meetup
Let's make our own concept
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;
// concept Number = std::is_integral_v<T> || std::is_floating_point_v<T>;
template<typename T>
requires Number<T>
T getMedianNumber(std::vector<T> values) {
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
Something that is satisfied or not, often treated "like a boolean"
Use concepts in requires clauses or to compose other concepts
std::integral and std::floating_point are built-in concepts
platis.solutions
©
for
GRCPP
Meetup
Trailing requires syntax is also possible:
template<typename T>
T getMedianNumber(std::vector<T> values)
requires Number<T> // <--- Trailing requires
{
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
It is exactly the same as the previous example:
template<typename T>
requires Number<T> // <--- Leading requires
T getMedianNumber(std::vector<T> values) {
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
platis.solutions
©
for
GRCPP
Meetup
Let's make getMedianNumber more readable
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template<Number T>
T getMedianNumber(std::vector<T> values) {
std::sort(values.begin(), values.end());
return values[values.size() / 2];
}
Use a concept as a non-type template parameter
Highly expressive and readable
Constrain the template parameter is a way that feels intuitive
platis.solutions
©
for
GRCPP
Meetup
concept vs requires
template<Number T>
requires std::is_integral_v<T> || std::is_floating_point_v<T>
T getMedianNumber1(std::vector<T> values) { /* ... */ }
template<typename T>
requires Number<T>
T getMedianNumber2(std::vector<T> values) { /* ... */ }
requires used to express requirements on template arguments
A concept is a named set of requirements
A concept is to a requires what a function is to a statement
In getMedianNumber2 we named the requirements Number
platis.solutions
©
for
GRCPP
Meetup
How do you requires ?
template<typename T>
concept Motor = requires(T m) { // <--- `requires` with curly braces
m.start();
m.stop();
};
template<typename T>
requires Motor<T> // <--- `requires` without curly braces
class Car {
// ...
};
Without curly braces: requires <some boolean expression>
With curly braces: requires(T m) { statements...; }
platis.solutions
©
for
GRCPP
Meetup
requires without curly braces
template<typename T>
requires std::is_constructible_v<T, std::string, int>
void createWithStringAndInt() { /* ... */ }
Expects a boolean expression to follow
If the boolean expression is true , requires is satisfied and valid
If the expression is false , requires is ill-formed
No error is generated if requires is ill-formed
May also be with parentheses: requires ( ... )
platis.solutions
©
for
GRCPP
Meetup
requires with curly braces
template<typename T>
concept StringAndIntConstructible = requires(std::string s, int i) {
T{s, i};
};
Expects a block of statements to follow {within curly braces}
Optionally preceded by objects for statement formulation
After type substitution if statements valid, requires is true
If any statement is ill-formed, requires evaluates to false
No error is generated if any statement is ill-formed
platis.solutions
©
for
GRCPP
Meetup
"Interfaces" for our template types
struct Motor {
Motor(int directionPin, int speedPin);
bool start();
bool stop();
};
class Car {
Motor mMotor{5 /* directionPin */, 10 /* speedPin */};
public:
void drive();
};
If Car was to become a template with Motor as a template type
would we ensure that Motor has start and stop functions?
platis.solutions
©
for
GRCPP
Meetup
Template "interfaces" without concepts: SFINAE
template<typename T, typename = void>
struct IsMotor : std::false_type {};
template<typename T>
struct IsMotor<T, std::void_t<decltype(std::declval<T>().start()),
decltype(std::declval<T>().stop())>>
: std::true_type {};
template<typename Motor>
class Car {
static_assert(IsMotor<Motor>::value, "Motor needs start and stop");
Motor mMotor;
public:
void drive();
};
platis.solutions
©
for
GRCPP
Meetup
Template "interfaces" with concepts
template<typename T>
concept Motor = requires(T m) {
T{int{}, int{}}; // Constructible with two ints
m.start(); // T has a public start method
m.stop(); // T has a public stop method
};
template<Motor M>
class Car {
M mMotor{5 /* directionPin */, 10 /* speedPin */};
public:
void drive();
};
Much simpler? Let's look at the requires expression.
platis.solutions
©
for
GRCPP
Meetup
requires as a "contract"
template <typename T>
concept Motor = requires(T m) {
m.start();
m.stop();
};
Evaluates to true if the expression is valid after substitution
false otherwise but no error is generated if ill-formed
Every line is a new "term" in the "contract", all must be satisfied
Do not see them as "commands" but as "terms in a contract"
Full syntax: cppreference.com/w/cpp/language/requires
platis.solutions
©
for
GRCPP
Meetup
requires requiring...
template <typename T>
concept Gyroscope = requires(T g, std::vector<int> params, int frequency) { // 1
T{params}; // 2
g.calibrate(); // 3
{ g.getAngle() } -> std::same_as<double>; // 4
g.setFrequency(frequency); // 5
};
1. "Objects" needed to express the requirements/statements
2. A constructor accepting a std::vector<int>
3. A calibrate() member function existing (return type unchecked)
4. getAngle() member function returning double
5. setFrequency(int) member function accepting an int
platis.solutions
©
for
GRCPP
Meetup
Verify getAngle that returns double exists with SFINAE:
template<typename T, typename = void>
struct HasGetAngle : std::false_type {};
template<typename T>
struct HasGetAngle<
T, std::enable_if_t<std::is_same<
double, decltype(std::declval<T>().getAngle())>::value>>
: std::true_type {};
// Alternatively:
// template<typename T>
// struct HasGetAngle<T, std::void_t<decltype(std::declval<T>().getAngle())>>
// : std::bool_constant<std::is_same<
// double, decltype(std::declval<T>().getAngle())>::value> {};
This is a lot of boilerplate code for a "simple" check.
platis.solutions
©
for
GRCPP
Meetup
More requires
template<typename T>
concept MyBigConcept = requires(T a, T b, std::ostream& out) {
a + b; // Addable with its own type
a++; // Incrementable
{ a == b } -> std::same_as<bool>; // Equality comparable
typename T::inner; // T::inner is a type (exists)
{ out << a } -> std::same_as<std::ostream&>; // Streamable to std::ostream
requires std::integral<typename T::value_type>; // T::value_type satisfies std::integral
{ a.size() } -> std::integral; // Return type satisfies other concept
{ T::Instances } -> std::same_as<std::size_t>; // T::Instances static and std::size_t
a.id; // `id` is a public member variable
};
platis.solutions
©
for
GRCPP
Meetup
Choosing the right candidate
template<typename Robot>
void handleEnemies(Robot) { std::cout << "I surrender!n"; }
template<typename Robot>
requires HasBullets<Robot>
void handleEnemies(Robot r) { r.shootBullets(); }
template<HasMissiles Robot>
void handleEnemies(Robot r) { r.shootMissiles(); }
struct RobotA { void shootBullets() { std::cout << "Bang!n"; } };
struct RobotB { void shootMissiles() { std::cout << "Shooosh!n"; } };
struct RobotC {};
handleEnemies(RobotA{}); // "Bang!"
handleEnemies(RobotB{}); // "Shooosh!"
handleEnemies(RobotC{}); // "I surrender!"
platis.solutions
©
for
GRCPP
Meetup
Specializing member functions
template<typename Motor>
concept HasOdometer = requires(Motor m) {
m.getPulses();
};
template<typename Motor>
struct Car {
void drive() { std::cout << "Driven"; }
void drive() requires HasOdometer<Motor> {
std::cout << "Drive with cruise controln";
}
};
The compiler chooses the most specialized member function.
The trailing requires clause becomes very useful here.
platis.solutions
©
for
GRCPP
Meetup
if constexpr and requires
template<typename T>
void print_info(T value) {
if constexpr (requires(int i) { value.foo(i); }) {
std::cout << "T has foo(int) member functionn";
} else if constexpr (requires { value.bar(); }) {
std::cout << "T has bar() member functionn";
} else {
std::cout << "T has neither foo(int) nor bar() member functionsn";
}
}
Create concepts on the fly with if constexpr and requires .
We may specify arguments in the requires clause.
platis.solutions
©
for
GRCPP
Meetup
What will be printed out?
template<typename T>
constexpr void print_type_info(const T& value) {
if constexpr (requires { std::is_integral_v<T>; }) {
std::cout << "Value is integral: " << value << std::endl;
} else {
std::cout << "Value is not integral" << std::endl;
}
}
print_type_info(5);
print_type_info(3.14);
print_type_info("Hello");
"Value is integral..." 3 times. Why?
Curly-braced requires becomes true if statements are valid
platis.solutions
©
for
GRCPP
Meetup
(Avoid) Concepts that are always satisfied
template<typename T>
concept AlwaysSatisfied1 = true;
template<typename T>
concept AlwaysSatisfied2 = requires { false; };
template<typename T>
concept AlwaysSatisfied3 = requires(T t) {
std::is_integral_v<T>;
std::is_floating_point_v<T>;
};
static_assert(AlwaysSatisfied1<int>); // Hardcoded to true
static_assert(AlwaysSatisfied2<int>); // `false;` is a valid statement
static_assert(AlwaysSatisfied3<int>); // `true;` and `false;` are valid
platis.solutions
©
for
GRCPP
Meetup
Which of the following constraints are always satisfied?
template<typename T>
concept Integral = requires {
std::integral<T>; // 1
requires std::integral<T>; // 2
std::is_integral_v<T>; // 3
{ T{} } -> std::integral; // 4
};
std::integral<T> always a valid expression ( true or false )
requires std::integral<T> becomes invalid if T is not integral
std::is_integral_v<T> always a valid expression ( true or false )
{ T{} } -> std::integral becomes invalid if T is not integral
platis.solutions
©
for
GRCPP
Meetup
requires { requires <true|false> }
template<typename T>
constexpr void print_type_info(const T& value) {
if constexpr (requires { requires std::is_integral_v<T>; }) {
std::cout << "Value is integral: " << value << std::endl;
} else {
std::cout << "Value is not integral" << std::endl;
}
}
requires without curly braces becomes valid if expression is true
requires std::is_integral_v<T>; is ill-formed if T not integral
requires with curly braces evaluates to true for valid statements
requires { ... }; is false if nested requires is ill-formed
platis.solutions
©
for
GRCPP
Meetup
requires requires { statements...; }
template<typename Container>
requires requires(Container a, Container::value_type v1, Container::value_type v2) {
{ a.begin() } -> std::input_iterator;
{ a.end() } -> std::sentinel_for<decltype(a.begin())>;
{ a.size() } -> std::same_as<std::size_t>;
{ v1 < v2 } -> std::same_as<bool>;
}
void print_sorted(Container& c) { /* ... */ }
requires with curly braces checks if the statements are valid
Becomes true if all statements are valid, false otherwise
requires without curly braces checks if the expression is true
Becomes valid if the expression is true , ill-formed otherwise
platis.solutions
©
for
GRCPP
Meetup
Concepts with multiple types
template<typename Motor, typename Odometer>
concept CompatibleOdometry = requires(Motor m, Odometer o) {
m.attach(o);
};
template<typename Motor, typename Odometer>
requires CompatibleOdometry<Motor, Odometer>
class Smartcar {
public:
Smartcar(Motor left, Motor right, Odometer odometer) { /* ... */ }
};
CompatibleOdometry requires Motor and Odometer to be compatible
Motor with member function attach accepting Odometer
platis.solutions
©
for
GRCPP
Meetup
Concepts with lambdas (no template parameter
list)
template<typename Car>
concept CanStop = requires(Car car) { car.stop(); };
template<typename Sensor>
concept CanDetectObstruction = requires(Sensor sensor) { sensor.isObstructed(); };
auto stopIfObstructed = [](auto& car, auto& sensor) -> void
requires CanDetectObstruction<decltype(sensor)> && CanStop<decltype(car)>
{
if (sensor.isObstructed()) { car.stop(); }
};
requires goes after the (optional) trailing return type
platis.solutions
©
for
GRCPP
Meetup
Concepts with lambdas (no template parameter
list)
auto stopIfObstructed2 = [](auto& car, auto& sensor)
requires requires {
{ sensor.isObstructed() } -> std::convertible_to<bool>;
car.stop();
}
{
if (sensor.isObstructed()) { car.stop(); }
};
Requirements on the fly with requires requires { statements...; }
platis.solutions
©
for
GRCPP
Meetup
Concepts with lambdas (template parameter list)
auto stop1 = []<typename Car, typename Sensor>
requires CanDetectObstruction<Sensor> && CanStop<Car>
(Car & car, Sensor & sensor) {
if (sensor.isObstructed()) { car.stop(); }
};
auto stop2 = []<CanStop Car, CanDetectObstruction Sensor>(Car& c, Sensor& s) {
if (s.isObstructed()) { c.stop(); }
};
requires after the lambda template parameter list
Can also go after the (optional) trailing return type
Concepts as non-type template parameters
platis.solutions
©
for
GRCPP
Meetup
Takeaways
Concepts are named sets of requirements on template types
concept is to requires what a function is to a statement
Two types of requires which can be confusing:
requires without curly braces
Expects a boolean expression, evaluates to valid or ill-formed
requires with curly braces
Expects a block of statements, evaluates to true or false
platis.solutions
©
for
GRCPP
Meetup
Takeaways
Concepts simplify code and provide better error messages
Use concepts to create "interfaces" for template classes & methods
Skip reading code or compiler errors to find the right type to use
Avoid cryptic and verbose SFINAE constructs
static_assert is still useful for providing custom error messages
platis.solutions
©
for
GRCPP
Meetup

More Related Content

Similar to [GRCPP] Introduction to concepts (C++20)

Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
CHOOSE
 
Cpp17 and Beyond
Cpp17 and BeyondCpp17 and Beyond
Cpp17 and Beyond
ComicSansMS
 
C++ Template
C++ TemplateC++ Template
C++ Template
Saket Pathak
 
Beyond C++17
Beyond C++17Beyond C++17
Beyond C++17
Mateusz Pusz
 
Advanced Programming C++
Advanced Programming C++Advanced Programming C++
Advanced Programming C++
guestf0562b
 
.NET 2015: Будущее рядом
.NET 2015: Будущее рядом.NET 2015: Будущее рядом
.NET 2015: Будущее рядом
Andrey Akinshin
 
Chapter 2
Chapter 2Chapter 2
Legacy is Good
Legacy is GoodLegacy is Good
Legacy is Good
Uberto Barbini
 
Bw14
Bw14Bw14
Java Generics
Java GenericsJava Generics
Java Generics
Carol McDonald
 
Csharp4 delegates lambda_and_events
Csharp4 delegates lambda_and_eventsCsharp4 delegates lambda_and_events
Csharp4 delegates lambda_and_events
Abed Bukhari
 
An Introduction To C++Templates
An Introduction To C++TemplatesAn Introduction To C++Templates
An Introduction To C++Templates
Ganesh Samarthyam
 
Practices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerPractices For Becoming A Better Programmer
Practices For Becoming A Better Programmer
Srikanth Shreenivas
 
Generic programming and concepts that should be in C++
Generic programming and concepts that should be in C++Generic programming and concepts that should be in C++
Generic programming and concepts that should be in C++
Anton Kolotaev
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
ShriKant Vashishtha
 
Savitch Ch 17
Savitch Ch 17Savitch Ch 17
Savitch Ch 17
Terry Yoast
 
The Goal and The Journey - Turning back on one year of C++14 Migration
The Goal and The Journey - Turning back on one year of C++14 MigrationThe Goal and The Journey - Turning back on one year of C++14 Migration
The Goal and The Journey - Turning back on one year of C++14 Migration
Joel Falcou
 
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
Andrey Upadyshev
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
Ganesh Samarthyam
 
Savitch ch 17
Savitch ch 17Savitch ch 17
Savitch ch 17
Terry Yoast
 

Similar to [GRCPP] Introduction to concepts (C++20) (20)

Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
Ralf Laemmel - Not quite a sales pitch for C# 3.0 and .NET's LINQ - 2008-03-05
 
Cpp17 and Beyond
Cpp17 and BeyondCpp17 and Beyond
Cpp17 and Beyond
 
C++ Template
C++ TemplateC++ Template
C++ Template
 
Beyond C++17
Beyond C++17Beyond C++17
Beyond C++17
 
Advanced Programming C++
Advanced Programming C++Advanced Programming C++
Advanced Programming C++
 
.NET 2015: Будущее рядом
.NET 2015: Будущее рядом.NET 2015: Будущее рядом
.NET 2015: Будущее рядом
 
Chapter 2
Chapter 2Chapter 2
Chapter 2
 
Legacy is Good
Legacy is GoodLegacy is Good
Legacy is Good
 
Bw14
Bw14Bw14
Bw14
 
Java Generics
Java GenericsJava Generics
Java Generics
 
Csharp4 delegates lambda_and_events
Csharp4 delegates lambda_and_eventsCsharp4 delegates lambda_and_events
Csharp4 delegates lambda_and_events
 
An Introduction To C++Templates
An Introduction To C++TemplatesAn Introduction To C++Templates
An Introduction To C++Templates
 
Practices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerPractices For Becoming A Better Programmer
Practices For Becoming A Better Programmer
 
Generic programming and concepts that should be in C++
Generic programming and concepts that should be in C++Generic programming and concepts that should be in C++
Generic programming and concepts that should be in C++
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
Savitch Ch 17
Savitch Ch 17Savitch Ch 17
Savitch Ch 17
 
The Goal and The Journey - Turning back on one year of C++14 Migration
The Goal and The Journey - Turning back on one year of C++14 MigrationThe Goal and The Journey - Turning back on one year of C++14 Migration
The Goal and The Journey - Turning back on one year of C++14 Migration
 
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
 
Savitch ch 17
Savitch ch 17Savitch ch 17
Savitch ch 17
 

More from Dimitrios Platis

OpenAI API crash course
OpenAI API crash courseOpenAI API crash course
OpenAI API crash course
Dimitrios Platis
 
Builder pattern in C++.pdf
Builder pattern in C++.pdfBuilder pattern in C++.pdf
Builder pattern in C++.pdf
Dimitrios Platis
 
Interprocess communication with C++.pdf
Interprocess communication with C++.pdfInterprocess communication with C++.pdf
Interprocess communication with C++.pdf
Dimitrios Platis
 
Lambda expressions in C++
Lambda expressions in C++Lambda expressions in C++
Lambda expressions in C++
Dimitrios Platis
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Dimitrios Platis
 
Introduction to CMake
Introduction to CMakeIntroduction to CMake
Introduction to CMake
Dimitrios Platis
 
Pointer to implementation idiom
Pointer to implementation idiomPointer to implementation idiom
Pointer to implementation idiom
Dimitrios Platis
 
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Dimitrios Platis
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)
Dimitrios Platis
 
[grcpp] Refactoring for testability c++
[grcpp] Refactoring for testability c++[grcpp] Refactoring for testability c++
[grcpp] Refactoring for testability c++
Dimitrios Platis
 
Refactoring for testability c++
Refactoring for testability c++Refactoring for testability c++
Refactoring for testability c++
Dimitrios Platis
 

More from Dimitrios Platis (11)

OpenAI API crash course
OpenAI API crash courseOpenAI API crash course
OpenAI API crash course
 
Builder pattern in C++.pdf
Builder pattern in C++.pdfBuilder pattern in C++.pdf
Builder pattern in C++.pdf
 
Interprocess communication with C++.pdf
Interprocess communication with C++.pdfInterprocess communication with C++.pdf
Interprocess communication with C++.pdf
 
Lambda expressions in C++
Lambda expressions in C++Lambda expressions in C++
Lambda expressions in C++
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
 
Introduction to CMake
Introduction to CMakeIntroduction to CMake
Introduction to CMake
 
Pointer to implementation idiom
Pointer to implementation idiomPointer to implementation idiom
Pointer to implementation idiom
 
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)
 
[grcpp] Refactoring for testability c++
[grcpp] Refactoring for testability c++[grcpp] Refactoring for testability c++
[grcpp] Refactoring for testability c++
 
Refactoring for testability c++
Refactoring for testability c++Refactoring for testability c++
Refactoring for testability c++
 

Recently uploaded

42 Ways to Generate Real Estate Leads - Sellxpert
42 Ways to Generate Real Estate Leads - Sellxpert42 Ways to Generate Real Estate Leads - Sellxpert
42 Ways to Generate Real Estate Leads - Sellxpert
vaishalijagtap12
 
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
Luigi Fugaro
 
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and MoreManyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
narinav14
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Paul Brebner
 
Boost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management AppsBoost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management Apps
Jhone kinadey
 
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data PlatformAlluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio, Inc.
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
kalichargn70th171
 
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
campbellclarkson
 
How GenAI Can Improve Supplier Performance Management.pdf
How GenAI Can Improve Supplier Performance Management.pdfHow GenAI Can Improve Supplier Performance Management.pdf
How GenAI Can Improve Supplier Performance Management.pdf
Zycus
 
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
The Third Creative Media
 
Building API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructureBuilding API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructure
confluent
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
dakas1
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
gapen1
 
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in NashikUpturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
Alina Yurenko
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
The Rising Future of CPaaS in the Middle East 2024
The Rising Future of CPaaS in the Middle East 2024The Rising Future of CPaaS in the Middle East 2024
The Rising Future of CPaaS in the Middle East 2024
Yara Milbes
 
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
widenerjobeyrl638
 

Recently uploaded (20)

42 Ways to Generate Real Estate Leads - Sellxpert
42 Ways to Generate Real Estate Leads - Sellxpert42 Ways to Generate Real Estate Leads - Sellxpert
42 Ways to Generate Real Estate Leads - Sellxpert
 
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
WMF 2024 - Unlocking the Future of Data Powering Next-Gen AI with Vector Data...
 
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and MoreManyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
 
Boost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management AppsBoost Your Savings with These Money Management Apps
Boost Your Savings with These Money Management Apps
 
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data PlatformAlluxio Webinar | 10x Faster Trino Queries on Your Data Platform
Alluxio Webinar | 10x Faster Trino Queries on Your Data Platform
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
 
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
🏎️Tech Transformation: DevOps Insights from the Experts 👩‍💻
 
How GenAI Can Improve Supplier Performance Management.pdf
How GenAI Can Improve Supplier Performance Management.pdfHow GenAI Can Improve Supplier Performance Management.pdf
How GenAI Can Improve Supplier Performance Management.pdf
 
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
Unlock the Secrets to Effortless Video Creation with Invideo: Your Ultimate G...
 
Building API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructureBuilding API data products on top of your real-time data infrastructure
Building API data products on top of your real-time data infrastructure
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
 
Upturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in NashikUpturn India Technologies - Web development company in Nashik
Upturn India Technologies - Web development company in Nashik
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
The Rising Future of CPaaS in the Middle East 2024
The Rising Future of CPaaS in the Middle East 2024The Rising Future of CPaaS in the Middle East 2024
The Rising Future of CPaaS in the Middle East 2024
 
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
美洲杯赔率投注网【​网址​🎉3977·EE​🎉】
 

[GRCPP] Introduction to concepts (C++20)

  • 1. concepts (since C++20) Create "interfaces" for your templates platis.solutions © for GRCPP Meetup
  • 2. About me Grew up in Rodos, Greece Software Engineering at GU & Chalmers Working with embedded systems Teaching DIT113, DAT265, Thesis supervision C++, Coursera, Udemy Open source projects https://platis.solutions https://github.com/platisd Email: dimitris@platis.solutions platis.solutions © for GRCPP Meetup
  • 3. Requirements on template arguments C++20 introduces constraints Specify requirements on template arguments Seamless selection of the appropriate overload or specialization Named sets of such requirements are called concepts Constraints and concepts requires expression platis.solutions © for GRCPP Meetup
  • 4. Why do we need concepts? template <typename Camera> class AutonomousCar { Camera mCamera; public: // ... A lot of code }; How do we ensure that Camera has all required functions? Normally, we read the Camera interface, with templates we can't. platis.solutions © for GRCPP Meetup
  • 5. Why do we need concepts? template <typename T> T getMedianNumber(std::vector<T> values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } What kind of types does it make sense for getMedian accept? What kind of types can getMedian accept? platis.solutions © for GRCPP Meetup
  • 6. Let's make our getMedianNumber more explicit template<typename T> T getMedianNumber(std::vector<T> values) { static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>, "T must be an integral or floating-point"); std::sort(values.begin(), values.end()); return values[values.size() / 2]; } static_assert is great, but can make things more readable? platis.solutions © for GRCPP Meetup
  • 7. Let's make our own constraint template<typename T> requires std::integral<T> || std::floating_point<T> // requires std::is_integral_v<T> || std::is_floating_point_v<T> T getMedianNumber(std::vector<T> values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } std::vector<std::string> files{"file22.txt", "file11.txt", "file33.txt"}; std::cout << getMedianNumber(files) << std::endl; // Compilation error std::vector numbers{0, 9, 5, 7, 3, 6, 2, 8, 1, 4, 10}; std::cout << getMedianNumber(numbers) << std::endl; // 5 "No operand of the disjunction is satisfied" requires std::integral<T> || std::floating_point<T> platis.solutions © for GRCPP Meetup
  • 8. Let's make our own concept template<typename T> concept Number = std::integral<T> || std::floating_point<T>; // concept Number = std::is_integral_v<T> || std::is_floating_point_v<T>; template<typename T> requires Number<T> T getMedianNumber(std::vector<T> values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } Something that is satisfied or not, often treated "like a boolean" Use concepts in requires clauses or to compose other concepts std::integral and std::floating_point are built-in concepts platis.solutions © for GRCPP Meetup
  • 9. Trailing requires syntax is also possible: template<typename T> T getMedianNumber(std::vector<T> values) requires Number<T> // <--- Trailing requires { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } It is exactly the same as the previous example: template<typename T> requires Number<T> // <--- Leading requires T getMedianNumber(std::vector<T> values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } platis.solutions © for GRCPP Meetup
  • 10. Let's make getMedianNumber more readable template<typename T> concept Number = std::integral<T> || std::floating_point<T>; template<Number T> T getMedianNumber(std::vector<T> values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } Use a concept as a non-type template parameter Highly expressive and readable Constrain the template parameter is a way that feels intuitive platis.solutions © for GRCPP Meetup
  • 11. concept vs requires template<Number T> requires std::is_integral_v<T> || std::is_floating_point_v<T> T getMedianNumber1(std::vector<T> values) { /* ... */ } template<typename T> requires Number<T> T getMedianNumber2(std::vector<T> values) { /* ... */ } requires used to express requirements on template arguments A concept is a named set of requirements A concept is to a requires what a function is to a statement In getMedianNumber2 we named the requirements Number platis.solutions © for GRCPP Meetup
  • 12. How do you requires ? template<typename T> concept Motor = requires(T m) { // <--- `requires` with curly braces m.start(); m.stop(); }; template<typename T> requires Motor<T> // <--- `requires` without curly braces class Car { // ... }; Without curly braces: requires <some boolean expression> With curly braces: requires(T m) { statements...; } platis.solutions © for GRCPP Meetup
  • 13. requires without curly braces template<typename T> requires std::is_constructible_v<T, std::string, int> void createWithStringAndInt() { /* ... */ } Expects a boolean expression to follow If the boolean expression is true , requires is satisfied and valid If the expression is false , requires is ill-formed No error is generated if requires is ill-formed May also be with parentheses: requires ( ... ) platis.solutions © for GRCPP Meetup
  • 14. requires with curly braces template<typename T> concept StringAndIntConstructible = requires(std::string s, int i) { T{s, i}; }; Expects a block of statements to follow {within curly braces} Optionally preceded by objects for statement formulation After type substitution if statements valid, requires is true If any statement is ill-formed, requires evaluates to false No error is generated if any statement is ill-formed platis.solutions © for GRCPP Meetup
  • 15. "Interfaces" for our template types struct Motor { Motor(int directionPin, int speedPin); bool start(); bool stop(); }; class Car { Motor mMotor{5 /* directionPin */, 10 /* speedPin */}; public: void drive(); }; If Car was to become a template with Motor as a template type would we ensure that Motor has start and stop functions? platis.solutions © for GRCPP Meetup
  • 16. Template "interfaces" without concepts: SFINAE template<typename T, typename = void> struct IsMotor : std::false_type {}; template<typename T> struct IsMotor<T, std::void_t<decltype(std::declval<T>().start()), decltype(std::declval<T>().stop())>> : std::true_type {}; template<typename Motor> class Car { static_assert(IsMotor<Motor>::value, "Motor needs start and stop"); Motor mMotor; public: void drive(); }; platis.solutions © for GRCPP Meetup
  • 17. Template "interfaces" with concepts template<typename T> concept Motor = requires(T m) { T{int{}, int{}}; // Constructible with two ints m.start(); // T has a public start method m.stop(); // T has a public stop method }; template<Motor M> class Car { M mMotor{5 /* directionPin */, 10 /* speedPin */}; public: void drive(); }; Much simpler? Let's look at the requires expression. platis.solutions © for GRCPP Meetup
  • 18. requires as a "contract" template <typename T> concept Motor = requires(T m) { m.start(); m.stop(); }; Evaluates to true if the expression is valid after substitution false otherwise but no error is generated if ill-formed Every line is a new "term" in the "contract", all must be satisfied Do not see them as "commands" but as "terms in a contract" Full syntax: cppreference.com/w/cpp/language/requires platis.solutions © for GRCPP Meetup
  • 19. requires requiring... template <typename T> concept Gyroscope = requires(T g, std::vector<int> params, int frequency) { // 1 T{params}; // 2 g.calibrate(); // 3 { g.getAngle() } -> std::same_as<double>; // 4 g.setFrequency(frequency); // 5 }; 1. "Objects" needed to express the requirements/statements 2. A constructor accepting a std::vector<int> 3. A calibrate() member function existing (return type unchecked) 4. getAngle() member function returning double 5. setFrequency(int) member function accepting an int platis.solutions © for GRCPP Meetup
  • 20. Verify getAngle that returns double exists with SFINAE: template<typename T, typename = void> struct HasGetAngle : std::false_type {}; template<typename T> struct HasGetAngle< T, std::enable_if_t<std::is_same< double, decltype(std::declval<T>().getAngle())>::value>> : std::true_type {}; // Alternatively: // template<typename T> // struct HasGetAngle<T, std::void_t<decltype(std::declval<T>().getAngle())>> // : std::bool_constant<std::is_same< // double, decltype(std::declval<T>().getAngle())>::value> {}; This is a lot of boilerplate code for a "simple" check. platis.solutions © for GRCPP Meetup
  • 21. More requires template<typename T> concept MyBigConcept = requires(T a, T b, std::ostream& out) { a + b; // Addable with its own type a++; // Incrementable { a == b } -> std::same_as<bool>; // Equality comparable typename T::inner; // T::inner is a type (exists) { out << a } -> std::same_as<std::ostream&>; // Streamable to std::ostream requires std::integral<typename T::value_type>; // T::value_type satisfies std::integral { a.size() } -> std::integral; // Return type satisfies other concept { T::Instances } -> std::same_as<std::size_t>; // T::Instances static and std::size_t a.id; // `id` is a public member variable }; platis.solutions © for GRCPP Meetup
  • 22. Choosing the right candidate template<typename Robot> void handleEnemies(Robot) { std::cout << "I surrender!n"; } template<typename Robot> requires HasBullets<Robot> void handleEnemies(Robot r) { r.shootBullets(); } template<HasMissiles Robot> void handleEnemies(Robot r) { r.shootMissiles(); } struct RobotA { void shootBullets() { std::cout << "Bang!n"; } }; struct RobotB { void shootMissiles() { std::cout << "Shooosh!n"; } }; struct RobotC {}; handleEnemies(RobotA{}); // "Bang!" handleEnemies(RobotB{}); // "Shooosh!" handleEnemies(RobotC{}); // "I surrender!" platis.solutions © for GRCPP Meetup
  • 23. Specializing member functions template<typename Motor> concept HasOdometer = requires(Motor m) { m.getPulses(); }; template<typename Motor> struct Car { void drive() { std::cout << "Driven"; } void drive() requires HasOdometer<Motor> { std::cout << "Drive with cruise controln"; } }; The compiler chooses the most specialized member function. The trailing requires clause becomes very useful here. platis.solutions © for GRCPP Meetup
  • 24. if constexpr and requires template<typename T> void print_info(T value) { if constexpr (requires(int i) { value.foo(i); }) { std::cout << "T has foo(int) member functionn"; } else if constexpr (requires { value.bar(); }) { std::cout << "T has bar() member functionn"; } else { std::cout << "T has neither foo(int) nor bar() member functionsn"; } } Create concepts on the fly with if constexpr and requires . We may specify arguments in the requires clause. platis.solutions © for GRCPP Meetup
  • 25. What will be printed out? template<typename T> constexpr void print_type_info(const T& value) { if constexpr (requires { std::is_integral_v<T>; }) { std::cout << "Value is integral: " << value << std::endl; } else { std::cout << "Value is not integral" << std::endl; } } print_type_info(5); print_type_info(3.14); print_type_info("Hello"); "Value is integral..." 3 times. Why? Curly-braced requires becomes true if statements are valid platis.solutions © for GRCPP Meetup
  • 26. (Avoid) Concepts that are always satisfied template<typename T> concept AlwaysSatisfied1 = true; template<typename T> concept AlwaysSatisfied2 = requires { false; }; template<typename T> concept AlwaysSatisfied3 = requires(T t) { std::is_integral_v<T>; std::is_floating_point_v<T>; }; static_assert(AlwaysSatisfied1<int>); // Hardcoded to true static_assert(AlwaysSatisfied2<int>); // `false;` is a valid statement static_assert(AlwaysSatisfied3<int>); // `true;` and `false;` are valid platis.solutions © for GRCPP Meetup
  • 27. Which of the following constraints are always satisfied? template<typename T> concept Integral = requires { std::integral<T>; // 1 requires std::integral<T>; // 2 std::is_integral_v<T>; // 3 { T{} } -> std::integral; // 4 }; std::integral<T> always a valid expression ( true or false ) requires std::integral<T> becomes invalid if T is not integral std::is_integral_v<T> always a valid expression ( true or false ) { T{} } -> std::integral becomes invalid if T is not integral platis.solutions © for GRCPP Meetup
  • 28. requires { requires <true|false> } template<typename T> constexpr void print_type_info(const T& value) { if constexpr (requires { requires std::is_integral_v<T>; }) { std::cout << "Value is integral: " << value << std::endl; } else { std::cout << "Value is not integral" << std::endl; } } requires without curly braces becomes valid if expression is true requires std::is_integral_v<T>; is ill-formed if T not integral requires with curly braces evaluates to true for valid statements requires { ... }; is false if nested requires is ill-formed platis.solutions © for GRCPP Meetup
  • 29. requires requires { statements...; } template<typename Container> requires requires(Container a, Container::value_type v1, Container::value_type v2) { { a.begin() } -> std::input_iterator; { a.end() } -> std::sentinel_for<decltype(a.begin())>; { a.size() } -> std::same_as<std::size_t>; { v1 < v2 } -> std::same_as<bool>; } void print_sorted(Container& c) { /* ... */ } requires with curly braces checks if the statements are valid Becomes true if all statements are valid, false otherwise requires without curly braces checks if the expression is true Becomes valid if the expression is true , ill-formed otherwise platis.solutions © for GRCPP Meetup
  • 30. Concepts with multiple types template<typename Motor, typename Odometer> concept CompatibleOdometry = requires(Motor m, Odometer o) { m.attach(o); }; template<typename Motor, typename Odometer> requires CompatibleOdometry<Motor, Odometer> class Smartcar { public: Smartcar(Motor left, Motor right, Odometer odometer) { /* ... */ } }; CompatibleOdometry requires Motor and Odometer to be compatible Motor with member function attach accepting Odometer platis.solutions © for GRCPP Meetup
  • 31. Concepts with lambdas (no template parameter list) template<typename Car> concept CanStop = requires(Car car) { car.stop(); }; template<typename Sensor> concept CanDetectObstruction = requires(Sensor sensor) { sensor.isObstructed(); }; auto stopIfObstructed = [](auto& car, auto& sensor) -> void requires CanDetectObstruction<decltype(sensor)> && CanStop<decltype(car)> { if (sensor.isObstructed()) { car.stop(); } }; requires goes after the (optional) trailing return type platis.solutions © for GRCPP Meetup
  • 32. Concepts with lambdas (no template parameter list) auto stopIfObstructed2 = [](auto& car, auto& sensor) requires requires { { sensor.isObstructed() } -> std::convertible_to<bool>; car.stop(); } { if (sensor.isObstructed()) { car.stop(); } }; Requirements on the fly with requires requires { statements...; } platis.solutions © for GRCPP Meetup
  • 33. Concepts with lambdas (template parameter list) auto stop1 = []<typename Car, typename Sensor> requires CanDetectObstruction<Sensor> && CanStop<Car> (Car & car, Sensor & sensor) { if (sensor.isObstructed()) { car.stop(); } }; auto stop2 = []<CanStop Car, CanDetectObstruction Sensor>(Car& c, Sensor& s) { if (s.isObstructed()) { c.stop(); } }; requires after the lambda template parameter list Can also go after the (optional) trailing return type Concepts as non-type template parameters platis.solutions © for GRCPP Meetup
  • 34. Takeaways Concepts are named sets of requirements on template types concept is to requires what a function is to a statement Two types of requires which can be confusing: requires without curly braces Expects a boolean expression, evaluates to valid or ill-formed requires with curly braces Expects a block of statements, evaluates to true or false platis.solutions © for GRCPP Meetup
  • 35. Takeaways Concepts simplify code and provide better error messages Use concepts to create "interfaces" for template classes & methods Skip reading code or compiler errors to find the right type to use Avoid cryptic and verbose SFINAE constructs static_assert is still useful for providing custom error messages platis.solutions © for GRCPP Meetup