Recherche…


Introduction

Voir aussi la rubrique séparée sur la résolution de la surcharge

Remarques

Des ambiguïtés peuvent se produire lorsqu'un type peut être implicitement converti en plusieurs types et qu'il n'y a pas de fonction correspondante pour ce type spécifique.

Par exemple:

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 

Qu'est-ce que la surcharge de fonctions?

La surcharge de fonctions fait en sorte que plusieurs fonctions déclarées dans la même étendue avec exactement le même nom existent au même endroit (appelé scope ) et ne diffèrent que par leur signature , ce qui signifie les arguments qu'elles acceptent.

Supposons que vous écrivez une série de fonctions pour des capacités d’impression généralisées, en commençant par std::string :

void print(const std::string &str)
{
    std::cout << "This is a string: " << str << std::endl;
}

Cela fonctionne bien, mais supposons que vous vouliez une fonction qui accepte également un int et l'imprime également. Vous pourriez écrire:

void print_int(int num)
{
    std::cout << "This is an int:  " << num << std::endl;
}

Mais comme les deux fonctions acceptent des paramètres différents, vous pouvez simplement écrire:

void print(int num)
{
    std::cout << "This is an int: " << num << std::endl;
}

Maintenant, vous avez 2 fonctions, toutes deux nommées print , mais avec des signatures différentes. L'un accepte std::string , l'autre un int . Maintenant, vous pouvez les appeler sans vous soucier des noms différents:

print("Hello world!"); //prints "This is a string: Hello world!"
print(1337);           //prints "This is an int: 1337"

Au lieu de:

print("Hello world!");
print_int(1337);

Lorsque vous avez des fonctions surchargées, le compilateur déduit quelles fonctions appeler à partir des paramètres que vous lui avez fournis. Des précautions doivent être prises lors de l'écriture de surcharges de fonctions. Par exemple, avec les conversions de types implicites:

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;
}

Maintenant, il n'est pas immédiatement clair quelle surcharge d' print est appelée lorsque vous écrivez:

print(5);

Et vous pourriez avoir besoin de donner à votre compilateur des indices, tels que:

print(static_cast<double>(5));
print(static_cast<int>(5));
print(5.0);

Des précautions doivent également être prises lors de l'écriture de surcharges acceptant des paramètres facultatifs:

// 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;
}

Comme il n'y a aucun moyen pour le compilateur de dire si un appel comme print(17) est destiné à la première ou à la deuxième fonction à cause du deuxième paramètre facultatif, la compilation échouera.

Type de retour en surcharge de fonction

Notez que vous ne pouvez pas surcharger une fonction en fonction de son type de retour. Par exemple:

// WRONG CODE
std::string getValue()
{
  return "hello";
}

int getValue()
{
  return 0;
}

int x = getValue();

Cela provoquera une erreur de compilation car le compilateur ne pourra pas déterminer quelle version de getValue appeler, même si le type de retour est assigné à un int .

Membre Fonction cv-qualifier Surcharge

Les fonctions d'une classe peuvent être surchargées lorsqu'elles sont accessibles via une référence qualifiée CV à cette classe; Ceci est le plus souvent utilisé pour surcharger const , mais peut également être utilisé pour surcharger des volatile et const volatile . En effet, toutes les fonctions membres non statiques prennent this comme paramètre caché, auquel les qualificateurs cv sont appliqués. Ceci est le plus couramment utilisé pour surcharger const , mais peut également être utilisé pour volatile et const volatile .

Cela est nécessaire car une fonction membre ne peut être appelée que si elle est au moins qualifiée cv par l'instance à laquelle elle est appelée. Alors qu'une instance non- const peut appeler des membres const et non- const , une instance const ne peut appeler que des membres const . Cela permet à une fonction d'avoir un comportement différent en fonction des qualificateurs cv de l'instance appelante et permet au programmeur d'interdire les fonctions pour un ou plusieurs qualificatifs cv indésirables en ne fournissant pas de version avec ce ou ces qualificatifs.

Une classe avec une méthode d' print base pourrait être surchargée en const comme suit:

#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"
}

C'est un principe clé de la correction des const : En marquant les fonctions membres comme const , elles peuvent être appelées sur des instances const , ce qui permet aux fonctions de prendre des instances en tant que pointeurs / références const si elles n'ont pas besoin de les modifier. Cela permet au code de spécifier s'il modifie l'état en prenant des paramètres non modifiés comme const et des paramètres modifiés sans qualificatifs cv, rendant le code à la fois plus sûr et plus lisible.

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.
}

Une utilisation courante de ceci est la déclaration des accesseurs en tant que const et les mutateurs en tant que non const .


Aucun membre de classe ne peut être modifié dans une fonction membre const . S'il y a un membre à modifier, comme le verrouillage d'un std::mutex , vous pouvez le déclarer 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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow