enable_if pada chapter sebelumnya bekerja seperti pengawas yang menutup pintu tanpa bicara: fungsi tidak cocok, selesai. C++20 menggantinya dengan concepts — kendala bernama yang dinyatakan langsung pada signature template. template <std::integral T> membaca seperti kalimat: “T harus tipe integral”. Bila pemanggil melanggar, compiler menunjuk kendala mana yang gagal dan mengapa. Chapter ini membawa kita dari trait yang dipakai diam-diam ke kendala yang dipakai terbuka — perubahan gaya terbesar pada pemrograman generik dalam satu dekade terakhir.
Concept: Kendala yang Bisa Dibaca #
Concept adalah predikat tentang tipe yang bernama dan bisa dipakai ulang. Standard library menyediakannya di header <concepts>; kita juga bisa menulis sendiri dengan menggabungkan yang sudah ada. Contoh berikut memakai std::integral sebagai kendala parameter, lalu mendefinisikan concept buatan bernama Numerik:
#include <concepts>
#include <iostream>
#include <string>
// Concept baku std::integral sebagai kendala parameter tipe
template <std::integral T>
T gandakan(T nilai) {
return nilai * 2;
}
// Concept buatan sendiri: gabungan dua concept baku
template <typename T>
concept Numerik = std::integral<T> || std::floating_point<T>;
template <Numerik T>
T setengah(T nilai) {
return nilai / 2;
}
// Kendala langsung pada parameter: std::integral auto
void tampilkan(std::integral auto nilai) {
std::cout << "tampilkan: " << nilai << "\n";
}
// Concept pada tipe kembalian
std::integral auto bacaInt() {
return 21;
}
int main() {
std::cout << "gandakan(21) = " << gandakan(21) << "\n";
std::cout << "setengah(9) = " << setengah(9) << "\n";
std::cout << "setengah(2.5) = " << setengah(2.5) << "\n";
tampilkan(7);
tampilkan('A');
std::integral auto nilai = bacaInt();
static_assert(std::same_as<decltype(nilai), int>);
std::cout << "nilai = " << nilai << "\n";
// gandakan(2.5); // error: tidak memenuhi std::integral
// setengah(std::string("x")); // error: std::string bukan Numerik
return 0;
}Output yang Dihasilkan:
gandakan(21) = 42
setengah(9) = 4
setengah(2.5) = 1.25
tampilkan: 7
tampilkan: A
nilai = 21Bedah baris demi baris:
- Baris 6 (
template <std::integral T>): kendala ditempel pada parameter tipe.gandakan(21)sah karenaintintegral;gandakan(2.5)akan ditolak — dan pesannya jauh lebih jelas daripadaenable_if, sebagaimana kita buktikan sebentar lagi. - Baris 12-13 (
concept Numerik = std::integral<T> || std::floating_point<T>;): concept buatan sendiri sebagai ekspresi boolean tentangT. Concept bisa menggabungkan concept lain dengan&&dan||, persis seperti logika biasa. - Baris 16-17:
setengahmenerima tipeNumerik. Pemanggilansetengah(9)menghasilkan4(pembagian integer), sedangkansetengah(2.5)menghasilkan1.25— satu template, dua perilaku yang sesuai tipe. - Baris 21 (
void tampilkan(std::integral auto nilai)): bentuk ringkas tanpatemplate <...>— parameter bertandaautoyang diberi kendala.tampilkan(7)dantampilkan('A')sama-sama sah karenacharjuga integral. - Baris 26-27 dan 38-39:
std::integral autosebagai tipe kembalian memaksa fungsi mengembalikan tipe integral, dan variabel bertanda sama juga terkendala.static_assert(std::same_as<decltype(nilai), int>)membuktikan deduksinya tepat menghasilkanint.
Concept Baku yang Perlu Dikenal #
Header <concepts> memuat puluhan concept; empat ini muncul hampir di semua kode modern:
| Concept | Pertanyaan yang dijawab |
|---|---|
std::integral<T> | apakah T tipe integer? |
std::floating_point<T> | apakah T tipe floating point? |
std::same_as<A, B> | apakah A dan B tipe yang sama persis? |
std::convertible_to<From, To> | apakah From bisa dikonversi ke To? |
Perhatikan hubungannya dengan type traits: std::integral didefinisikan berdasarkan std::is_integral_v — kita melihatnya langsung dalam pesan error di akhir chapter. Concept pada dasarnya adalah lapisan bahasa yang lebih ekspresif di atas alat pemeriksaan yang sudah kita kuasai.
Mendefinisikan concept dari nol memakai requires expression — misalnya kendala “T punya method size()” atau “a + b sah” — dan karena mekanismenya layak dibedah pelan-pelan, ia mendapat chapter tersendiri: requires Expressions.
auto dengan Concept #
Tiga posisi penulisan kendala yang kita pakai di atas punya nama masing-masing:
template <std::integral T> void f(T x)— kendala pada parameter template, lalu tipe dideklarasikan dariT.void f(std::integral auto x)— bentuk ringkas; compiler diam-diam membuat parameter template sendiri. Keduanya identik hasilnya.std::integral auto y = f();— kendala pada deklarasi variabel atau tipe kembalian; bila fungsi mengembalikandouble, pemanggil ditolak.
Bentuk auto-berkendala ini paling praktis untuk fungsi biasa dan lambda, sedangkan bentuk template <std::integral T> wajib bila T perlu dipakai lebih dari satu kali atau dijadikan tipe kembalian.
Pesan Error yang Menjelaskan Diri Sendiri #
Sekarang saatnya membuktikan klaim utama chapter ini. Aktifkan baris yang tadi kita komentar:
#include <concepts>
template <std::integral T>
T gandakan(T nilai) {
return nilai * 2;
}
int main() {
gandakan(2.5);
}Potongan pesan nyata dari g++ 14.2 (dipersingkat):
concepterr.cpp: In function 'int main()':
concepterr.cpp:9:13: error: no matching function for call to 'gandakan(double)'
9 | gandakan(2.5);
| ~~~~~~~~^~~~~
concepterr.cpp:4:3: note: candidate: 'template<class T> requires integral<T> T gandakan(T)'
4 | T gandakan(T nilai) {
| ^~~~~~~~
concepterr.cpp:4:3: note: template argument deduction/substitution failed:
concepterr.cpp:4:3: note: constraints not satisfied
concepterr.cpp:9:13: required from here
9 | gandakan(2.5);
| ~~~~~~~~^~~~~
/usr/include/c++/14/concepts:107:13: required for the satisfaction of 'integral<T>' [with T = double]
/usr/include/c++/14/concepts:107:24: note: the expression 'is_integral_v<_Tp> [with _Tp = double]' evaluated to 'false'
107 | concept integral = is_integral_v<_Tp>;
| ^~~~~~~~~~~~~~~~~~Bandingkan dengan pesan enable_if yang berhenti di “no matching function”. Kini compiler menelusuri rantai kendala sampai akarnya: candidate gandakan menuntut integral<T>, dan untuk T = double ekspresi is_integral_v bernilai false. Pemula bisa membaca tiga baris terakhir dan langsung paham. Tidak ada kesalahan pengetikan yang harus dicari — kendala yang tidak terpenuhi memang tidak terpenuhi.
Concept mengubah cara kita mengumumkan syarat. Langkah berikutnya adalah menulis syarat yang lebih spesifik: bukan “tipe integral”, melainkan “tipe yang punya method ini dan operasi itu”. Alatnya requires expression — topik chapter berikutnya.
Referensi resmi: cppreference — Constraints and concepts dan cppreference — std::integral .