C++ Standard Concepts
A standard concept is a ready-made rule from the standard library. The common ones cover whole numbers, decimal numbers, and class links.
std::integral guards a template
The twiceValue template accepts whole number types only. An int call and a long call both pass the check.
#include <iostream>
#include <concepts>
template <std::integral T>
T twiceValue(T value) {
return value * 2;
}
int main() {
std::cout << twiceValue(21) << std::endl;
std::cout << twiceValue(100000L) << std::endl;
return 0;
}Two concepts pick two overloads
Two describe templates share one name. The int call meets std::integral, and the double call meets std::floating_point.
#include <iostream>
#include <concepts>
template <std::integral T>
void describe(T value) {
std::cout << "Whole: " << value << std::endl;
}
template <std::floating_point T>
void describe(T value) {
std::cout << "Decimal: " << value << std::endl;
}
int main() {
describe(7);
describe(2.5);
return 0;
}A concept built from standard parts
The ScoreLike concept asks that a type can become a double. It also rules out bool, which converts but is not a score.
#include <iostream>
#include <concepts>
template <typename T>
concept ScoreLike = std::convertible_to<T, double> && !std::same_as<T, bool>;
template <ScoreLike T>
void printScore(T value) {
double score = value;
std::cout << score << std::endl;
}
int main() {
printScore(85);
printScore(92.5);
return 0;
}std::derived_from checks a class link
The IsAnimal concept asks that a type derives from Animal. Dog derives from Animal, so the call is accepted.
#include <iostream>
#include <string>
#include <concepts>
class Animal {
public:
std::string name;
};
class Dog : public Animal {
};
template <typename T>
concept IsAnimal = std::derived_from<T, Animal>;
template <IsAnimal T>
void printName(T value) {
std::cout << value.name << std::endl;
}
int main() {
Dog dog;
dog.name = "Rex";
printName(dog);
return 0;
}Example
#include <iostream>
#include <concepts>
template <std::integral T>
void describe(T value) {
std::cout << "Whole: " << value << std::endl;
}
template <std::floating_point T>
void describe(T value) {
std::cout << "Decimal: " << value << std::endl;
}
int main() {
describe(7);
describe(2.5);
describe(100L);
return 0;
}Key points
- Use std::integral for whole number types such as int and long.
- Use std::floating_point for decimal number types such as double.
- Use std::same_as to check that two types are exactly the same type.
- Use std::convertible_to to check that one type can become another type.
- Use std::derived_from to check that a class comes from a base class.
- Two overloads can share a name when their concepts accept different types.
In this example, the describe family sorts three calls by concept. The int call and the long call print with the Whole label. The double call prints with the Decimal label.
Note
The standard concepts live in the <concepts> header, so a program includes it before use.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which standard concept accepts int and long but refuses double?