サーチ…
前書き
過負荷解決に関する別のトピックも参照してください。
備考
あるタイプを複数のタイプに暗黙的に変換することができ、その特定のタイプに対して一致する機能がない場合、曖昧さが発生する可能性があります。
例えば:
void foo(double, double);
void foo(long, long);
//Call foo with 2 ints
foo(1, 2); //Function call is ambiguous - int can be converted into a double/long at the same time
関数のオーバーロードとは何ですか?
関数のオーバーロードでは、同じスコープ内で宣言された複数の関数が同じ場所に存在する( スコープと呼ばれます )、 シグネチャだけが異なります。
std::string
で始まる一般化された印刷機能の一連の関数を書いているとします。
void print(const std::string &str)
{
std::cout << "This is a string: " << str << std::endl;
}
これは正常に動作しますが、 int
を受け取り、それも出力する関数が必要であるとします。あなたは書くことができます:
void print_int(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
しかし、2つの関数が異なるパラメータを受け入れるので、簡単に書くことができます:
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
今度はprint
という名前の2つの関数がありますが、異なる署名があります。 1つはstd::string
受け取り、もう1つはint
受け取ります。今すぐあなたは別の名前について心配することなくそれらを呼び出すことができます:
print("Hello world!"); //prints "This is a string: Hello world!"
print(1337); //prints "This is an int: 1337"
の代わりに:
print("Hello world!");
print_int(1337);
オーバーロードされた関数があると、コンパイラはそれを提供するパラメータからどの関数を呼び出すかを推測します。関数のオーバーロードを記述するときは注意が必要です。たとえば、暗黙的な型変換の場合:
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
void print(double num)
{
std::cout << "This is a double: " << num << std::endl;
}
今すぐあなたが書くときにprint
オーバーロードが呼び出されるのはすぐにはわかりません:
print(5);
コンパイラにいくつかの手がかりを与える必要があるかもしれません:
print(static_cast<double>(5));
print(static_cast<int>(5));
print(5.0);
オプションのパラメータを受け入れるオーバーロードを記述するときには、いくつか注意が必要です。
// WRONG CODE
void print(int num1, int num2 = 0) //num2 defaults to 0 if not included
{
std::cout << "These are ints: << num1 << " and " << num2 << std::endl;
}
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}
print(17)
ような呼び出しがオプションの第2引数のために第1または第2の関数のために意図されているかどうかをコンパイラが判断する方法はないので、コンパイルに失敗します。
関数のオーバーロードでの戻り値の型
戻り値の型に基づいて関数をオーバーロードすることはできません。例えば:
// WRONG CODE
std::string getValue()
{
return "hello";
}
int getValue()
{
return 0;
}
int x = getValue();
戻り値の型がint
割り当てられていても、コールするgetValue
バージョンをコンパイラが処理できないため、コンパイルエラーが発生します。
メンバー関数cv-qualifierオーバーロード
クラス内の関数は、そのクラスへのcv修飾参照を介してアクセスされたときにオーバーロードされます。これは最も一般的にはconst
オーバーロードに使用されますが、 volatile
とconst volatile
オーバーロードにも使用できます。これは、すべての非スタティックメンバ関数がcv修飾子が適用される非表示のパラメータとしてthis
をとるためです。これはconst
オーバーロードに最も一般的に使用されますが、 volatile
およびconst volatile
でも使用できます。
メンバー関数は、呼び出されたインスタンスと少なくともcv修飾されている場合にのみ呼び出すことができるため、これは必要です。非ながらconst
インスタンスが両方呼び出すことができるconst
と非const
メンバーを、 const
インスタンスにのみ呼び出すことができますconst
メンバー。これにより、関数は呼び出し側のインスタンスのcv修飾子によって異なる動作をすることができ、プログラマは、その修飾子を持つバージョンを提供しないことによって、望ましくないcv修飾子の関数を禁止することができます。
いくつかの基本的なprint
メソッドを持つクラスは、次のようにconst
オーバーロードすることができます:
#include <iostream>
class Integer
{
public:
Integer(int i_): i{i_}{}
void print()
{
std::cout << "int: " << i << std::endl;
}
void print() const
{
std::cout << "const int: " << i << std::endl;
}
protected:
int i;
};
int main()
{
Integer i{5};
const Integer &ic = i;
i.print(); // prints "int: 5"
ic.print(); // prints "const int: 5"
}
これは、 const
正確さの重要な指針です。メンバ関数をconst
としてマークすることによって、 const
インスタンスに対して呼び出すことができます。変更する必要がない場合、関数はconst
ポインタ/参照としてインスタンスを取ります。これにより、変更されていないパラメータをconst
として、cv修飾子なしで変更されたパラメータを取り、より安全で読みやすいコードにすることで、コードが状態を変更するかどうかを指定することができます。
class ConstCorrect
{
public:
void good_func() const
{
std::cout << "I care not whether the instance is const." << std::endl;
}
void bad_func()
{
std::cout << "I can only be called on non-const, non-volatile instances." << std::endl;
}
};
void i_change_no_state(const ConstCorrect& cc)
{
std::cout << "I can take either a const or a non-const ConstCorrect." << std::endl;
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Error. Can only be called from non-const instance.
}
void const_incorrect_func(ConstCorrect& cc)
{
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Good. Can only be called from non-const instance.
}
これの一般的な使い方は、アクセッサをconst
として、mutatorを非const
として宣言することです。
const
メンバ関数内でクラスメンバを変更することはできません。 std::mutex
をロックするなど、本当に変更が必要なメンバーがある場合は、 mutable
として宣言できます。
class Integer
{
public:
Integer(int i_): i{i_}{}
int get() const
{
std::lock_guard<std::mutex> lock{mut};
return i;
}
void set(int i_)
{
std::lock_guard<std::mutex> lock{mut};
i = i_;
}
protected:
int i;
mutable std::mutex mut;
};