Ch16: Concepts¶
TL;DR: Template Constraints: Concepts (C++20) ช่วยกำหนดเงื่อนไขจำกัดพารามิเตอร์ของ Template ในระดับฟังก์ชันลายเซ็นโดยตรง ทำให้อ่านง่ายขึ้นแทนการใช้ Type Traits และ
static_assertแบบเก่า
⚡ Quick Reference¶
#include <iostream>
#include <concepts>
#include <type_traits>
// ประกาศ Custom Concept โดยใช้งาน Type Traits สำเร็จรูป
template <typename T>
concept MyConcept = std::is_integral_v<T>;
// ประกาศ Custom Concept โดยระบุตรรกะในบล็อก requires
template <typename T>
concept Addable = requires(T a, T b) {
a + b; // Simple requirement: ตรวจสอบความถูกต้องทางไวยากรณ์
};
// การใช้ Nested และ Compound Requirement ภายใน Concept
template <typename T>
concept TinyAndConvertible = requires(T a) {
requires sizeof(T) <= 4; // Nested requirement: บังคับให้ตรวจสอบเงื่อนไขบูลีนเป็นจริง
{ a + 1 } -> std::convertible_to<int>; // Compound requirement: ตรวจไวยากรณ์พร้อมเช็คชนิดผลลัพธ์แปลงข้ามได้
};
// ไวยากรณ์รูปแบบที่ 1: กำกับข้อจำกัดต่อท้ายบรรทัดประกาศเทมเพลต
template <typename T>
requires std::integral<T>
T add1(T a, T b) { return a + b; }
// ไวยากรณ์รูปแบบที่ 2: เขียนชื่อ Concept แทนคำว่า typename ในพารามิเตอร์เทมเพลต
template <std::integral T>
T add2(T a, T b) { return a + b; }
// ไวยากรณ์รูปแบบที่ 3: บล็อกพารามิเตอร์แบบ auto ด้วยเงื่อนไขของ Concept
auto add3(std::integral auto a, std::integral auto b) { return a + b; }
// ไวยากรณ์รูปแบบที่ 4: เขียนข้อกำหนดดักต่อท้ายพารามิเตอร์ของฟังก์ชัน
template <typename T>
T add4(T a, T b) requires std::integral<T> { return a + b; }
// นำเสนอเงื่อนไขร่วมกันโดยผ่านตัวดำเนินการ OR / AND
template <typename T>
requires std::integral<T> || std::floating_point<T>
void print_num(T val) {
// ดักควบคุมออบเจกต์จำพวก auto ให้เป็นไปตามเงื่อนไขที่กำหนด
std::integral auto copy = val;
}
int main() {
return 0;
}
🧠 Core Concepts¶
- Template Constraints: Concepts (C++20) ช่วยกำหนดเงื่อนไขจำกัดพารามิเตอร์ของ Template ในระดับฟังก์ชันลายเซ็นโดยตรง ทำให้อ่านง่ายขึ้นแทนการใช้ Type Traits และ
static_assertแบบเก่า - Requirements Clauses: Custom Concept สามารถตั้งเงื่อนไขได้หลายรูปแบบ ได้แก่ Simple (ตรวจไวยากรณ์), Nested (ตรวจผลตรรกะ boolean), และ Compound (ตรวจไวยากรณ์พร้อมตรวจสอบประเภทของผลลัพธ์) ภายใต้คำสั่ง
requires - Flexible Syntaxes: เราสามารถนำเสนอเงื่อนไข Concept ได้ในหลายตำแหน่ง เช่น เขียนแทน
typenameในรายการพารามิเตอร์, เขียนต่อท้ายฟังก์ชันเป็นrequiresclause, หรือใส่หน้าพารามิเตอร์ที่เป็นauto
⚠️ Pitfalls (Quick Scan)¶
| ข้อผิดพลาด | วิธีแก้ |
|---|---|
การใช้เงื่อนไขตรวจสอบธรรมดา (Simple Requirement เช่น sizeof(T) <= 4) เพื่อวัดค่าความจริง |
ครอบเงื่อนไขตรวจสอบด้วยคำสั่ง requires ซ้อนด้านใน (Nested Requirement) เพื่อบังคับเช็คค่าจริง |
ลืมไปว่าการบวกตัวแปรชนิดเล็ก (เช่น char) จะกระตุ้นการขยายเป็น int อัตโนมัติ |
พิมพ์ค่า Return แบบกำหนดประเภทตายตัว หรือใช้การแปลงชนิดข้อมูลช่วยปรับค่าขาส่งคืน |
เข้าใจผิดว่าการตรวจสอบไวยากรณ์คำสั่ง (เช่น a b) จะช่วยการันตีความหมายทางตรรกะ |
ออกแบบ Concept ควบคู่กับการใช้ Type Traits เพื่อจำกัดชนิดข้อมูลให้ปลอดภัยยิ่งขึ้น |
| เขียนนิยามบล็อก Concept ส่วนตัวซ้อนกันอยู่ในบรรทัดเดียว (Inline Concept) | แยกเขียนโครงสร้าง Concept แยกบรรทัดกันภายนอก แล้วเรียกใช้งานผ่านชื่ออ้างอิงแทน |
| พึ่งพาการแสดงผลขีดเส้นใต้เตือนปัญหาของตัวแก้ไขโค้ด (IDE) เพียงอย่างเดียว | ตรวจสอบข้อความแจ้งเตือนจากการสร้างระบบโดย Compiler อย่าง GCC เพื่อความชัดเจน |
📖 Full Details¶
Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู)
* **การใช้เงื่อนไขตรวจสอบธรรมดา (Simple Requirement เช่น `sizeof(T) <= 4`) เพื่อวัดค่าความจริง** -> **Compiler ตรวจสอบแค่ว่าไวยากรณ์โค้ดถูกต้องไหม ทำให้ยอมรับประเภทข้อมูลที่มีขนาดใหญ่กว่าเกณฑ์ส่งเข้ามาได้** -> **ครอบเงื่อนไขตรวจสอบด้วยคำสั่ง `requires` ซ้อนด้านใน (Nested Requirement) เพื่อบังคับเช็คค่าจริง (13:58)** * **ลืมไปว่าการบวกตัวแปรชนิดเล็ก (เช่น `char`) จะกระตุ้นการขยายเป็น `int` อัตโนมัติ** -> **ผลลัพธ์ขาส่งคืน (Return type) อาจหลุดข้อจำกัดขนาดของ Concept ที่ตั้งไว้** -> **พิมพ์ค่า Return แบบกำหนดประเภทตายตัว หรือใช้การแปลงชนิดข้อมูลช่วยปรับค่าขาส่งคืน (18:50)** * **เข้าใจผิดว่าการตรวจสอบไวยากรณ์คำสั่ง (เช่น `a * b`) จะช่วยการันตีความหมายทางตรรกะ** -> **Compiler ยอมรันโค้ดหากไวยากรณ์ผ่าน แม้ว่าชนิดข้อมูลนั้นจะนำมาคูณกันแล้วไม่ได้ตรรกะที่สมเหตุสมผล** -> **ออกแบบ Concept ควบคู่กับการใช้ Type Traits เพื่อจำกัดชนิดข้อมูลให้ปลอดภัยยิ่งขึ้น (09:35)** * **เขียนนิยามบล็อก Concept ส่วนตัวซ้อนกันอยู่ในบรรทัดเดียว (Inline Concept)** -> **ทำให้ลายเซ็นของฟังก์ชันมีความรกรุงรัง อ่านและทำความเข้าใจได้ยากมาก** -> **แยกเขียนโครงสร้าง Concept แยกบรรทัดกันภายนอก แล้วเรียกใช้งานผ่านชื่ออ้างอิงแทน (26:10)** * **พึ่งพาการแสดงผลขีดเส้นใต้เตือนปัญหาของตัวแก้ไขโค้ด (IDE) เพียงอย่างเดียว** -> **คำอธิบายแจ้งเตือนของ IDE มักจะกำกวมและระบุส่วนย่อยที่แครชได้ไม่เจาะจง** -> **ตรวจสอบข้อความแจ้งเตือนจากการสร้างระบบโดย Compiler อย่าง GCC เพื่อความชัดเจน (05:30)**📎 Repo Files¶
25.02UsingConcepts/main.cpp25.03BuildingYourOwnConcepts/main.cpp25.04ZoomingInOnRequiresClause/main.cpp25.05CombiningConcepts/main.cpp25.06ConceptsAndAuto/main.cpp