अंतिम अद्यतन:2026-10-12
C++20 Concepts अवधारणा¶
C++20 ने concept पेश किया, जो टेम्प्लेट पैरामीटर (compile-समय मूल्यांकन) पर एक सेट के constraints हैं। आप इन्हें class templates और function templates पर उपयोग कर function overloading और specialization को नियंत्रित कर सकते हैं। कुछ फ़ायदों में शामिल हैं:
- टेम्प्लेट पैरामीटर पर type constraints लागू करना
- कोड की पठनीयता बढ़ाना (लंबे SFINAE कोड की जगह)
- अधिक अनुकूल त्रुटि संदेश देना
- उपयोग योग्य प्रकारों को सीमित कर अनजाने template instantiation से बचाना
एक सरल जोड़ (addition) का उदाहरण लेते हैं।
#include <iostream>
struct Foo {};
template <typename T>
T Add(T a, T b) {
return a + b;
}
int main() {
std::cout << Add(1, 2) << std::endl;
Foo f1, f2;
std::cout << Add(f1, f2) << std::endl;
return 0;
}
Foo के लिए, addition समर्थित नहीं है, और साथ ही सीधे std::cout << भी नहीं कर सकते, इसलिए compile के समय ढेर सारी त्रुटियाँ आती हैं, जिनमें operator<< और operator+ शामिल हैं, लेकिन यह हमारी अपेक्षित त्रुटि संदेश नहीं है, हम चाहते हैं कि कंपाइलर हमें सबसे सहज त्रुटि संदेश दे, यानी: यह संरचना जुड़ सकती है या नहीं।
add.cc: In function 'int main()':
add.cc:13:13: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'Foo')
13 | std::cout << Add(f1, f2) << std::endl;
| ~~~~~~~~~ ^~ ~~~~~~~~~~~
| | |
| | Foo
| std::ostream {aka std::basic_ostream<char>}
In file included from /usr/local/Cellar/gcc/13.2.0/include/c++/13/iostream:41,
from add.cc:1:
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:110:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(__ostream_type& (*)(__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]'
110 | operator<<(__ostream_type& (*__pf)(__ostream_type&))
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:801:5: note: template argument deduction/substitution failed:
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream: In substitution of 'template<class _Ostream, class _Tp> _Ostream&& std::operator<<(_Ostream&&, const _Tp&) [with _Ostream = std::basic_ostream<char>&; _Tp = Foo]':
add.cc:13:26: required from here
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:801:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
add.cc: In instantiation of 'T Add(T, T) [with T = Foo]':
add.cc:13:19: required from here
add.cc:7:12: error: no match for 'operator+' (operand types are 'Foo' and 'Foo')
7 | return a + b;
| ~~^~~
जब हम concept से लागू करते हैं:
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
template <typename T>
requires Addable<T>
T Add(T a, T b) {
return a + b;
}
तो हमें हमारी परवाह की त्रुटि मिलती है:
add_concept.cc: In function 'int main()':
add_concept.cc:17:19: error: no matching function for call to 'Add(Foo&, Foo&)'
17 | std::cout << Add(f1, f2) << std::endl;
| ~~~^~~~~~~~
add_concept.cc:10:3: note: candidate: 'template<class T> requires Addable<T> T Add(T, T)'
10 | T Add(T a, T b) {
| ^~~
add_concept.cc:10:3: note: template argument deduction/substitution failed:
add_concept.cc:10:3: note: constraints not satisfied
add_concept.cc: In substitution of 'template<class T> requires Addable<T> T Add(T, T) [with T = Foo]':
add_concept.cc:17:19: required from here
add_concept.cc:6:9: required for the satisfaction of 'Addable<T>' [with T = Foo]
add_concept.cc:6:19: in requirements with 'T a', 'T b' [with T = Foo]
add_concept.cc:6:42: note: the required expression '(a + b)' is invalid
6 | concept Addable = requires(T a, T b) { a + b; };
| ~~^~~
नीचे, हम ऊपर वाले उदाहरण के साथ concept सिंटैक्स गहराई से सीखते हैं।
1.concept सिंटैक्स¶
सिंटैक्स:
हम वास्तविक उदाहरण से तुलना करते हैं, Addable concept-name है, constraint-expression है requires(T a, T b) { a + b; }।
उपयोग का तरीका:
इस concept को कई जगह रखा जा सकता है, जैसे:
- typename की जगह
- requires के बाद
- auto के पहले
1.1 typename की जगह¶
टेम्प्लेट पैरामीटर constraint देना, typename की जगह।
1.2 requires कीवर्ड¶
हम function template में requires कीवर्ड का उपयोग करते हैं। यह हमारे टेम्प्लेट T के जुड़ने योग्य होने तक पहुँच सकता है, यदि टेम्प्लेट addition संभाल सकता है, तो यह true लौटाएगा।
requires को टेम्प्लेट में रखा जा सकता है, और फ़ंक्शन के बाद भी, लेकिन class के बाद नहीं। साथ ही इसके दो रूप हैं:
- requires शर्त
उदाहरण के लिए:
- requires एक्सप्रेशन
उदाहरण के लिए:
1.2.1 टेम्प्लेट में रखना¶
फ़ंक्शन:
क्लास:
1.2.2 फ़ंक्शन का trailing Requires क्लॉज़¶
फ़ंक्शन:
क्लास के लिए यह लेखन-शैली समर्थित नहीं है, त्रुटि मिलेगी: error: expected unqualified-id before 'requires' 28 | requires Addable
1.2.3 requires मूल उदाहरण¶
डेटाबेस के प्रकारों का उदाहरण लें, डेटाबेस में भिन्न प्रकार होते हैं, हम उन्हें इस तरह बाँटते हैं: null, binary, number आदि, हम पास किए गए प्रकार पर print ऑपरेशन चाहते हैं, इसलिए यह उदाहरण बना:
#include <concepts>
class NumberType {};
class BaseBinaryType {};
class NullType {};
class FloatingPointType : public NumberType {};
class IntegerType : public NumberType {};
class BinaryType: public BaseBinaryType {};
template <typename T>
requires std::is_base_of_v<NumberType, T> || std::is_base_of_v<BaseBinaryType, T>
void PrintValue(T v) {}
int main() {
PrintValue(FloatingPointType{});
PrintValue(NullType{});
return 0;
}
requires में हम || का उपयोग कर सकते हैं, ऊपर के उदाहरण में NullType आया है, वह requires पूरा नहीं करता, इसलिए compile होगा:
concept_requires.cc:16:13: error: no matching function for call to 'PrintValue(NullType)'
16 | PrintValue(NullType{});
| ~~~~~~~~~~^~~~~~~~~~~~
concept_requires.cc:12:6: note: candidate: 'template<class T> requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T)'
12 | void PrintValue(T v) {}
| ^~~~~~~~~~
concept_requires.cc:12:6: note: template argument deduction/substitution failed:
concept_requires.cc:12:6: note: constraints not satisfied
concept_requires.cc: In substitution of 'template<class T> requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T) [with T = NullType]':
concept_requires.cc:16:13: required from here
concept_requires.cc:12:6: required by the constraints of 'template<class T> requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T)'
concept_requires.cc:11:45: note: no operand of the disjunction is satisfied
11 | requires std::is_base_of_v<NumberType, T> || std::is_base_of_v<BaseBinaryType, T>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.3 concept और auto¶
जब auto आए, हम उसे concept के साथ उपयोग कर सकते हैं, उदाहरण के लिए:
हम इसे ऐसे कर सकते हैं:
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
auto add2(Addable auto x, Addable auto y) {
return x + y;
}
Compile के समय आएगा:
concept_auto.cc:17:20: error: no matching function for call to 'add2(Foo&, Foo&)'
17 | std::cout << add2(f1, f2) << std::endl;
| ~~~~^~~~~~~~
concept_auto.cc:10:6: note: candidate: 'template<class auto:18, class auto:19> requires (Addable<auto:18>) && (Addable<auto:19>) auto add2(auto:18, auto:19)'
10 | auto add2(Addable auto x, Addable auto y) {
| ^~~~
concept_auto.cc:10:6: note: template argument deduction/substitution failed:
concept_auto.cc:10:6: note: constraints not satisfied
concept_auto.cc: In substitution of 'template<class auto:18, class auto:19> requires (Addable<auto:18>) && (Addable<auto:19>) auto add2(auto:18, auto:19) [with auto:18 = Foo; auto:19 = Foo]':
concept_auto.cc:17:20: required from here
concept_auto.cc:8:9: required for the satisfaction of 'Addable<auto:18>' [with auto:18 = Foo]
concept_auto.cc:8:19: in requirements with 'T a', 'T b' [with T = Foo]
concept_auto.cc:8:42: note: the required expression '(a + b)' is invalid
8 | concept Addable = requires(T a, T b) { a + b; };
2.कंपाइलर समर्थन¶
GCC (10.0+), Clang (10.0+) चाहिए, compile विकल्प: -std=c++20/-std=c++2a
3.सारांश¶
C++20 द्वारा दिए गए concept के बाद, हमें अब enable_if/SFINAE तंत्र और function overloading से टेम्प्लेट constraint जाँचने की ज़रूरत नहीं, concept इस काम को संभाल लेता है, यह टेम्प्लेट पैरामीटर constraints का एक स्पष्ट और शक्तिशाली तंत्र देता है, जिससे टेम्प्लेट कोड लिखना, समझना और रखरखाव आसान हो जाता है। Compile समय type checking से, यह कोड की मज़बूती और पठनीयता बढ़ाने में मदद करता है।
संबंधित विषय¶
- C++20 coroutines परिचय | C++那些事
- C++20 channel व्यवहारिक उपयोग | C++那些事
- C++20 constexpr और comma एक्सप्रेशन | C++那些事
- C++20 byte order endian | C++那些事
- C++20 Lambda एक्सप्रेशन (भाग 1) | C++那些事
- C++20 Modules मॉड्यूल | C++那些事
- C++20 Ranges लाइब्रेरी | C++那些事
- C++20 std::span डिज़ाइन और जाल | C++那些事
- C++20 नई विशेषताओं की जानकारी | C++那些事
- C++20 [[likely]] और [[unlikely]] | C++那些事