ภาพรวม Function Overloading
Function Overloading (การโอเวอร์โหลดฟังก์ชัน) คือคุณสมบัติของ C++ ที่อนุญาตให้ประกาศฟังก์ชันหลายฟังก์ชันที่มี ชื่อเดียวกัน แต่มี Parameter List (ลายเซ็น) ต่างกัน
กติกาที่ทำให้ Overload ได้
- ชื่อฟังก์ชันเหมือนกัน
- Parameter List ต่างกัน (จำนวน ตัวหรือ ชนิดข้อมูล)
- Return type ไม่ถูกนับเป็นส่วนหนึ่งของ Signature
ฟังก์ชันที่ ไม่สามารถ Overload ได้
1. ฟังก์ชันที่ต่างกันเฉพาะ Return Type
// ❌ ผิด - คอมไพเลอร์ Error: redefinition int func(int a); float func(int a); // เหมือนกัน parameter แตกต่าง return type เท่านั้น
2. ฟังก์ชันที่มี Default Arguments ทำให้ Signature ชนกัน
// ❌ ผิด - เรียก func(10) คอมไพเลอร์ไม่รู้เรียกตัวไหน void func(int a, int b); void func(int a, int b = 10); // เรียก func(10) ได้ทั้งคู่
3. Static Member Function กับ Non-Static Member Function ที่เหมือนกัน
class MyClass {
static void func(int x); // ❌ ไม่สามารถ overload กับ
void func(int x); // non-static version ได้
};
4. ฟังก์ชันที่ต่างกันแค่ cv-qualifiers (const/volatile) ของพารามิเตอร์ที่เป็น reference/pointer ระดับบนสุด
// ❌ ผิด - ถือว่า signature เหมือนกัน void func(int* p); void func(const int* p); // top-level const ถูกละเว้น
แต่ สำหรับ reference/pointer ระดับใน (non-top-level) overload ได้:
// ✓ ถูกต้อง - overload ได้ void func(int* p); void func(const int* p); // wait, this is top-level... // ✓ ถูกต้องจริง: void func(int& r); void func(const int& r); // reference to const - overload ได้
5. ฟังก์ชันที่ต่างกันแค่ typedef / using alias
typedef int MyInt; void func(int a); void func(MyInt a); // ❌ ผิด - MyInt คือ int เหมือนกัน
ฟังก์ชันที่ Overload ได้ (สรุป)
| กรณี | ตัวอย่าง | ผลลัพธ์ |
|---|---|---|
| จำนวนพารามิเตอร์ต่างกัน | func(int), func(int, int) | ✓ OK |
| ชนิดพารามิเตอร์ต่างกัน | func(int), func(float) | ✓ OK |
| ลำดับพารามิเตอร์ต่างกัน | func(int, float), func(float, int) | ✓ OK |
| cv-qualifiers บน reference/pointer ระดับใน | func(int&), func(const int&) | ✓ OK |
| Ref vs Value | func(int), func(int&) | ✓ OK |
| R-value reference | func(int&), func(int&&) | ✓ OK (C++11) |
ตัวอย่างโค้ดสาธิต
#include <iostream>
using namespace std;
// ✓ Overload ได้: จำนวน parameter ต่างกัน
void print(int a) { cout << "One int: " << a << endl; }
void print(int a, int b) { cout << "Two ints: " << a << ", " << b << endl; }
// ✓ Overload ได้: ชนิด parameter ต่างกัน
void print(double a) { cout << "One double: " << a << endl; }
void print(const string& s) { cout << "String: " << s << endl; }
// ✓ Overload ได้: ลำดับ parameter ต่างกัน
void print(int a, double b) { cout << "int, double: " << a << ", " << b << endl; }
void print(double a, int b) { cout << "double, int: " << a << ", " << b << endl; }
// ✓ Overload ได้: const reference
void modify(int& x) { cout << "Non-const ref: " << x << endl; }
void modify(const int& x) { cout << "Const ref: " << x << endl; }
// ❌ ไม่ได้: เฉพาะ return type ต่างกัน
// int getValue();
// double getValue(); // Error!
// ❌ ไม่ได้: default argument ทำให้ ambiguous
// void func(int a, int b);
// void func(int a, int b = 10);
int main() {
print(10);
print(10, 20);
print(3.14);
print("Hello");
print(1, 2.5);
print(2.5, 1);
int x = 5;
modify(x); // เรียก non-const version
modify(10); // เรียก const version (rvalue bind to const ref)
return 0;
}
ผลลัพธ์ตัวอย่าง
One int: 10 Two ints: 10, 20 One double: 3.14 String: Hello int, double: 1, 2.5 double, int: 2.5, 1 Non-const ref: 5 Const ref: 10
Best Practices
- Overload เฉพาะเมื่อทำหน้าที่ "เหมือนกัน" แต่รับข้อมูลต่างชนิดกัน
- หลีกเลี่ยง Default Arguments ที่ทำให้เกิด Ambiguity
- ใช้
explicitสำหรับ Constructor เพื่อป้องกัน Implicit Conversion ที่ทำให้ Overload Resolution ยาก - Document ชัดเจนว่าแต่ละ overload ทำอะไรต่างกันอย่างไร
Overload Resolution (การคัดเลือก Overload)
C++ ใช้กติกา Best Match:
- Exact Match (ไม่ต้อง conversion)
- Promotion (int → double, float → double)
- Conversion (int → float, double → int)
- User-defined Conversion
- Ellipsis (...) - ตัวเลือกสุดท้าย
ถ้ามีหลายฟังก์ชันเท่าเทียมกัน → Ambiguity Error