Ce face concept
Un concept este o condiție despre un tip, evaluată la compilare: „T este un număr întreg”, „T se poate aduna”, „T are metoda size()”. Îl folosești în locul lui typename: template <std::integral T> acceptă doar tipuri întregi.
Avantajul principal: dacă apelezi șablonul cu un tip nepotrivit, eroarea spune clar ce cerință nu este îndeplinită, în loc de zeci de rânduri despre interiorul șablonului. Codul devine și documentație: cerințele se văd în semnătură.
Biblioteca standard are multe concepte gata făcute în <concepts>: std::integral, std::floating_point, std::same_as, std::convertible_to, std::totally_ordered. Conceptele proprii se definesc de obicei cu o expresie requires.
Exemple
Exemplul 1: Concepte standard în semnătură
Forma scurtă std::integral auto este echivalentă cu un șablon cu parametru constrâns. jumatate("text") ar da o eroare clară: const char* nu satisface std::floating_point.
#include <concepts>
#include <print>
template <std::integral T>
bool este_par(T n) {
return n % 2 == 0;
}
double jumatate(std::floating_point auto x) {
return x / 2;
}
int main() {
std::println("10 este par: {}", este_par(10));
std::println("7L este par: {}", este_par(7L));
std::println("Jumătate din 5.5: {}", jumatate(5.5));
}
Rezultat
10 este par: true
7L este par: false
Jumătate din 5.5: 2.75
Exemplul 2: Concept propriu
AreArie cere ca tipul să aibă o metodă aria() al cărei rezultat se poate transforma în double. Ambele structuri o au, deci sunt acceptate.
#include <concepts>
#include <print>
template <typename T>
concept AreArie = requires(const T& forma) {
{ forma.aria() } -> std::convertible_to<double>;
};
struct Patrat { double l; double aria() const { return l * l; } };
struct Triunghi { double b, h; double aria() const { return b * h / 2; } };
template <AreArie T>
void raport(const T& forma) {
std::println("Aria: {:.2f}", forma.aria());
}
int main() {
raport(Patrat{3});
raport(Triunghi{4, 5});
std::println("int are arie: {}", AreArie<int>);
}
Rezultat
Aria: 9.00
Aria: 10.00
int are arie: false
Greșeli frecvente
- Definești conceptul prea larg (de exemplu doar „are
+”) și apoi folosești în șablon și alte operații. Conceptul nu verifică restul, deci erorile vechi, lungi, revin. - Încerci să definești un concept în interiorul unei funcții sau al unei clase. Conceptele se definesc doar la nivel de spațiu de nume.
Comenzi înrudite
requires: Scrie cerințe pentru parametrii unui șablon: ca clauză care restrânge șablonul sau ca expresie care verifică dacă un cod este valid.template: Scrie o funcție sau o clasă o singură dată, pentru orice tip; compilatorul generează varianta potrivită pentru fiecare tip folosit.if constexpr: Un if evaluat la compilare: ramura falsă este eliminată complet, deci poate conține cod care nu s-ar compila pentru tipul curent.static_assert: Verifică o condiție la compilare; dacă este falsă, compilarea se oprește cu mesajul dat.std::integral: Concepte C++20 care restrâng un șablon la tipuri întregi (integral) sau reale (floating_point).