ข้ามไปที่เนื้อหา

Ch15: Function Templates

TL;DR: Template Blueprints: Function Template ทำหน้าที่เป็นโครงร่าง (Blueprint) สำหรับสร้างโค้ด โดย Compiler จะแปลงเป็นโค้ดจริง (Instantiation) เฉพาะกับชนิดข้อมูลที่มีการเรียกใช้งานตอน Compile เท่านั้น

⚡ Quick Reference

#include <iostream>
#include <type_traits>
#include <cstring>

// template <typename T> - ประกาศแบบแผนอินเตอร์เฟซเทมเพลตสำหรับแทนค่าประเภทข้อมูล T
// T maximum(T a, T b) - เนื้อหาฟังก์ชันต้นแบบทั่วไปใช้แทนค่าประเภท T
template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// template <> const char* maximum<const char*>(...) - สเปกจำเพาะของเทมเพลตสำหรับข้อมูลประเภท const char*
template <>
const char* maximum<const char*>(const char* a, const char* b) {
    return std::strcmp(a, b) > 0 ? a : b;
}

// const T& - รับส่งอาร์กิวเมนต์แบบอ้างอิงคงที่หลีกเลี่ยงการก๊อปปี้
template <typename T>
const T& maximum_ref(const T& a, const T& b) {
    return (a > b) ? a : b;
}

// ประกาศเทมเพลตแบบหลายประเภทข้อมูลร่วมกับระบุขาส่งกลับชัดเจน
template <typename ReturnType, typename T, typename P>
ReturnType mix_add(T a, P b) {
    return a + b;
}

// การประยุกต์ใช้ auto return type และ trailing return type ร่วมกับ decltype
template <typename T, typename P>
auto mix_add_deduced(T a, P b) -> decltype((a > b) ? a : b) {
    return (a > b) ? a : b;
}

// decltype(auto) - คาดเดาประเภทโดยประคับประคองสถานะ reference
template <typename T>
decltype(auto) get_ref_value(T& ref) {
    return ref;
}

// ฟังก์ชันเทมเพลตย่อของ C++20 โดยใช้พารามิเตอร์เป็น auto
auto generic_auto(auto a, auto b) {
    return a + b;
}

// Non-type template parameter (NTTP)
template <typename T, size_t size>
bool is_valid(T arr[size]) {
    return size > 0;
}

int main() {
    // static_assert - ตรวจสอบคุณสมบัติข้อมูลขณะประมวลผลช่วง Compile-time
    static_assert(std::is_integral_v<int>, "Integer required");

    // maximum<T>(a, b) - เรียกฟังก์ชันโดยระบุประเภทอาร์กิวเมนต์ตรงตัว
    auto res = maximum<double>(10, 20.5);

    // if constexpr - คำสั่งแยกเงื่อนไขทำงานในระดับ Compile-time
    if constexpr (sizeof(int) == 4) {
        // โค้ดส่วนนี้จะทำงานต่อเมื่อเป็นระบบแบบ 32-bit int เท่านั้น
    }

    return 0;
}

🧠 Core Concepts

  • Template Blueprints: Function Template ทำหน้าที่เป็นโครงร่าง (Blueprint) สำหรับสร้างโค้ด โดย Compiler จะแปลงเป็นโค้ดจริง (Instantiation) เฉพาะกับชนิดข้อมูลที่มีการเรียกใช้งานตอน Compile เท่านั้น
  • Type Deduction & Homogeneity: Compiler จะคาดเดาประเภทข้อมูลอาร์กิวเมนต์โดยอัตโนมัติ แต่พารามิเตอร์ประเภท T ร่วมกันในฟังก์ชันต้องประเมินออกมาเป็นประเภทเดียวกัน (Homogeneous) เสมอ
  • Specialization & Customization: Full Template Specialization ช่วยให้เราสามารถออกแบบตรรกะเฉพาะเจาะจงกับประเภทข้อมูลบางตัวได้ (เช่น const char*) ซึ่งจะแซงหน้าตรรกะทั่วไปของ Template

⚠️ Pitfalls (Quick Scan)

ข้อผิดพลาด วิธีแก้
ส่งอาร์กิวเมนต์คนละประเภทไปยัง Template ที่รับพารามิเตอร์เดี่ยว T เรียกใช้งานโดยระบุประเภทตรงๆ เช่น maximum<double>(a, c)
ใช้ตัวดำเนินการที่ไม่รองรับภายในบล็อกคำสั่งของ Template มั่นใจว่าชนิดข้อมูลทุกตัวที่ส่งเข้ามาสนับสนุนตัวดำเนินการภายในลูป
ส่งข้อมูลประเภท const char เข้าไปประมวลผลใน Template เปรียบเทียบค่าทั่วไป เขียนสเปกจำเพาะ (Specialization) โดยใช้ std::strcmp สำหรับ const char*
ประกาศทำ Overload ชนิดรับส่งตามมูลค่า (T) คู่กับรับส่งอ้างอิง (const T&) เลือกวิธีการรับส่งพารามิเตอร์แบบใดแบบหนึ่ง แนะนำใช้ const T&
พยายามแก้ไขค่าตัวแปรใน Template ที่รับพารามิเตอร์เป็นแบบ const T& หลีกเลี่ยงการเปลี่ยนแปลงค่าตัวแปรประเภทคงที่
กำหนดประเภทของตัวแปรรับค่าฟังก์ชันแคบกว่าชนิดผลลัพธ์ของ Template ใช้ Keyword auto ในการประกาศตัวแปรมารับผลลัพธ์การประมวลผล
ส่งพอยน์เตอร์ไปยังฟังก์ชัน Template ที่เขียนขึ้นเพื่อเปรียบเทียบค่าข้อมูล ถอดตัวอ้างอิงพอยน์เตอร์ (Dereference) ก่อนส่ง หรือเขียน specialization
คาดหวังให้การตรวจสอบ Compile ตรวจเจอข้อผิดพลาดใน Template ที่ไม่ได้ใช้งาน เรียกใช้งานฟังก์ชันล่วงหน้าด้วยประเภทข้อมูลต่างๆ เพื่อดักจับบั๊ก

📖 Full Details

Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู) * **ส่งอาร์กิวเมนต์คนละประเภทไปยัง Template ที่รับพารามิเตอร์เดี่ยว `T`** -> **เกิด Compile-time error แจ้งเตือนว่าหาข้อตกลงประเภทข้อมูลขัดแย้งกัน** -> **เรียกใช้งานโดยระบุประเภทตรงๆ เช่น `maximum(a, c)` (20:50)** * **ใช้ตัวดำเนินการที่ไม่รองรับภายในบล็อกคำสั่งของ Template** -> **เกิด Compile-time error แจ้งว่าไม่มีเครื่องหมายที่จับคู่กับชนิดข้อมูลนั้น** -> **มั่นใจว่าชนิดข้อมูลทุกตัวที่ส่งเข้ามาสนับสนุนตัวดำเนินการภายในลูป (09:30)** * **ส่งข้อมูลประเภท `const char*` เข้าไปประมวลผลใน Template เปรียบเทียบค่าทั่วไป** -> **เกิดการเปรียบเทียบแอดเดรสของพอยน์เตอร์แทนเนื้อหาข้อความจริง** -> **เขียนสเปกจำเพาะ (Specialization) โดยใช้ `std::strcmp` สำหรับ `const char*` (14:30)** * **ประกาศทำ Overload ชนิดรับส่งตามมูลค่า (`T`) คู่กับรับส่งอ้างอิง (`const T&`)** -> **เกิด Compile-time error ตอนเรียกใช้งาน เนื่องจากไม่รู้ว่าต้องไปฟังก์ชันตัวใด** -> **เลือกวิธีการรับส่งพารามิเตอร์แบบใดแบบหนึ่ง แนะนำใช้ `const T&` (28:10)** * **พยายามแก้ไขค่าตัวแปรใน Template ที่รับพารามิเตอร์เป็นแบบ `const T&`** -> **เกิด Compile-time error เนื่องจากตัวแปรอ้างอิงคงที่ห้ามแก้ไข** -> **หลีกเลี่ยงการเปลี่ยนแปลงค่าตัวแปรประเภทคงที่ (28:40)** * **กำหนดประเภทของตัวแปรรับค่าฟังก์ชันแคบกว่าชนิดผลลัพธ์ของ Template** -> **เกิดการปัดทิ้งเศษทศนิยมเงียบๆ (เช่น 78.7 กลายเป็น 78) ทำให้สูญเสียความแม่นยำ** -> **ใช้ Keyword `auto` ในการประกาศตัวแปรมารับผลลัพธ์การประมวลผล (07:10)** * **ส่งพอยน์เตอร์ไปยังฟังก์ชัน Template ที่เขียนขึ้นเพื่อเปรียบเทียบค่าข้อมูล** -> **ตัวลูปจะเปรียบเทียบค่าที่อยู่หน่วยความจำแทนที่จะเทียบค่าข้างใน** -> **ถอดตัวอ้างอิงพอยน์เตอร์ (Dereference) ก่อนส่ง หรือเขียน specialization (14:30)** * **คาดหวังให้การตรวจสอบ Compile ตรวจเจอข้อผิดพลาดใน Template ที่ไม่ได้ใช้งาน** -> **Compiler จะไม่ประเมินและทิ้งโค้ดเหล่านั้นไปเลยหากไม่มีการ Instantiate** -> **เรียกใช้งานฟังก์ชันล่วงหน้าด้วยประเภทข้อมูลต่างๆ เพื่อดักจับบั๊ก (02:22)**

📎 Repo Files

  • 24.FunctionTemplates/24.2TryingOutFunctionTemplates/main.cpp
  • 24.FunctionTemplates/24.3TemplateTypeDeductionAndExplicitArguments/main.cpp
  • 24.FunctionTemplates/24.4TemplateTypeParametersByReference/main.cpp
  • 24.FunctionTemplates/24.5TemplateSpecialization/main.cpp
  • 24.FunctionTemplates/24.6FunctionTemplatesWithOverloading/main.cpp
  • 24.FunctionTemplates/24.7FunctionTemplatesWithMultipleParameters/main.cpp
  • 24.FunctionTemplates/24.8TemplateReturnTypeDeductionWithAuto/main.cpp
  • 24.FunctionTemplates/24.9DecltypeAndTrailingReturnTypes/main.cpp
  • 24.FunctionTemplates/24.10DecltypeAuto/main.cpp
  • 24.FunctionTemplates/24.11DefaultArguments/main.cpp
  • 24.FunctionTemplates/24.12NonTypeTemplateParameters/main.cpp
  • 24.FunctionTemplates/24.13AutoFunctionTemplates/main.cpp
  • 24.FunctionTemplates/24.14NamedTemplateParametersForLambdas/main.cpp
  • 24.FunctionTemplates/24.15TypeTraits/main.cpp
  • 24.FunctionTemplates/24.16ConstexprIf/main.cpp