サーチ…


前書き

過負荷解決に関する別のトピックも参照してください。

備考

あるタイプを複数のタイプに暗黙的に変換することができ、その特定のタイプに対して一致する機能がない場合、曖昧さが発生する可能性があります。

例えば:

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オーバーロードに使用されますが、 volatileconst 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;
};


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow