関数オーバーロード

同じ名前で異なる処理。C++の多態性を実現

概要

関数オーバーロードは、 同じ名前で複数の関数を定義することができる機能です。

引数の型や個数が異なれば、 呼び出すときに自動的に正しい関数が選ばれます。

基本例

例:add 関数の複数バージョン

#include <iostream>

// int版
int add(int a, int b) {
    return a + b;
}

// double版
double add(double a, double b) {
    return a + b;
}

// 3つの数を足す版(int)
int add(int a, int b, int c) {
    return a + b + c;
}

int main() {
    std::cout << add(2, 3) << std::endl;            // 5(int版)
    std::cout << add(2.5, 3.5) << std::endl;       // 6(double版)
    std::cout << add(2, 3, 4) << std::endl;        // 9(3つの数版)
    
    return 0;
}

5
6
9

同じ名前addでも、 引数の型と個数を見て適切な関数が呼ばれます。

オーバーロードの条件

オーバーロードが成立するには、関数のシグニチャが異なる必要があります。

条件 OK? 理由
add(int, int)add(double, double) OK 引数の型が異なる
add(int, int)add(int, int, int) OK 引数の個数が異なる
add(int, double)add(double, int) OK 引数の順序が異なる(型が異なる)
int add(int, int)double add(int, int) NG 戻り値の型だけが異なる(呼び出し時に判断できない)

実践例

例1: 最大値を求める

#include <iostream>

// 2つの int の最大値
int maximum(int a, int b) {
    return (a > b) ? a : b;
}

// 2つの double の最大値
double maximum(double a, double b) {
    return (a > b) ? a : b;
}

// 3つの int の最大値
int maximum(int a, int b, int c) {
    int maxAB = maximum(a, b);
    return (maxAB > c) ? maxAB : c;
}

int main() {
    std::cout << maximum(10, 20) << std::endl;       // 20
    std::cout << maximum(3.5, 2.5) << std::endl;    // 3.5
    std::cout << maximum(5, 10, 3) << std::endl;    // 10
    
    return 0;
}

20
3.5
10

例2: 属性値を設定する

#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    int age;
    double height;
    
    void setInfo(std::string n) {
        name = n;
    }
    
    void setInfo(int a) {
        age = a;
    }
    
    void setInfo(double h) {
        height = h;
    }
};

int main() {
    Person p;
    p.setInfo("太郎");  // 名前を設定
    p.setInfo(25);      // 年齢を設定(int)
    p.setInfo(175.5);   // 身長を設定(double)
    
    std::cout << p.name << ", " << p.age << ", " << p.height << std::endl;
    return 0;
}

太郎, 25, 175.5

参照を使ったオーバーロード

例:const参照 vs 非const参照

#include <iostream>

void printValue(const int & x) {
    std::cout << "const版: " << x << std::endl;
}

void printValue(int & x) {
    std::cout << "非const版: " << x << std::endl;
}

int main() {
    int num = 42;
    printValue(num);  // 非const版を呼び出す
    
    const int const_num = 42;
    printValue(const_num);  // const版を呼び出す
    
    return 0;
}

非const版: 42
const版: 42

型変換による呼び出し

完全一致がない場合、 コンパイラは型変換を試みます。

#include <iostream>

void display(int x) {
    std::cout << "int版: " << x << std::endl;
}

void display(double x) {
    std::cout << "double版: " << x << std::endl;
}

int main() {
    display(5);         // int版(完全一致)
    display(5.5);       // double版(完全一致)
    display(5L);        // int版(long から int へ変換)
    
    return 0;
}

実践例:計算機関数

#include <iostream>

int power(int base, int exponent) {
    int result = 1;
    for (int i = 0; i < exponent; i++) {
        result *= base;
    }
    return result;
}

double power(double base, int exponent) {
    double result = 1.0;
    for (int i = 0; i < exponent; i++) {
        result *= base;
    }
    return result;
}

int main() {
    std::cout << "2^3 = " << power(2, 3) << std::endl;        // 8
    std::cout << "2.5^3 = " << power(2.5, 3) << std::endl;   // 15.625
    
    return 0;
}

2^3 = 8
2.5^3 = 15.625

オーバーロード解析のルール

関数が呼び出された時、以下の順序で適切な関数が選ばれます:

  1. 完全一致:引数と戻り値の型が完全に一致
  2. 標準変換:int → long、bool → int など
  3. ユーザー定義変換:クラスのコンストラクタなど

複数の関数が同じ優先順位で候補になった場合は エラー(曖昧)になります。

ポイント

  • オーバーロードで同じ名前で複数の処理を実装
  • 引数の型と個数が異なる必要がある
  • 戻り値の型だけでは区別されない
  • 呼び出し時に自動的に正しい関数が選ばれる
  • 型変換が行われることに注意

よくある誤り

誤り1: 戻り値の型だけが異なる

int getValue() { return 42; }
double getValue() { return 42.0; }  // エラー!: 呼び出し時に判断できない

誤り2: 曖昧なオーバーロード

void process(int x) { }
void process(long x) { }  // 曖昧
// process(42) がどちらか判断できない場合がある

誤り3: namespace や class の境界を越えた呼び出し

namespace ns1 {
    void func(int x) { }
}

namespace ns2 {
    void func(double x) { }
}

int main() {
    func(42);  // どちらが呼ばれる?(曖昧)
    ns1::func(42);  // これで明確
}

やってみよう

練習1: 面積を計算する関数。正方形、円、三角形に対応。

練習2: 入力を受け取る関数。int と string の2バージョン。

練習3: 複数の add 関数。2つ、3つ、4つの数を足す。

チャレンジ: abs 関数の複数バージョン(int, double, long)。

まとめ

  • 関数オーバーロードで同じ操作に対して型別の実装
  • 引数の型と個数で関数を区別
  • 戻り値の型のみ異なる場合はオーバーロード不可
  • 適切に使うと可読性が向上し、使いやすいAPI