Поиск…


Вступление

См. Также отдельную тему о разрешении перегрузки

замечания

Неоднозначность может возникать, когда один тип может быть неявно преобразован в несколько типов, и для этого конкретного типа нет соответствующей функции.

Например:

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

Но поскольку две функции принимают разные параметры, вы можете просто написать:

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

Теперь у вас есть 2 функции, оба с именем print , но с разными сигнатурами. Один принимает std::string , другой - 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) для первой или второй функции из-за необязательного второго параметра, это не скомпилируется.

Тип возврата в функции Перегрузка

Обратите внимание, что вы не можете перегружать функцию на основе ее возвращаемого типа. Например:

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

int getValue()
{
  return 0;
}

int x = getValue();

Это вызовет ошибку компиляции, поскольку компилятор не сможет определить, какую версию getValue вызывать, даже если тип возвращаемого значения присваивается int .

Функция члена cv-qualifier Перегрузка

Функции внутри класса могут быть перегружены, когда они доступны через cv-квалифицированную ссылку на этот класс; это чаще всего используется для перегрузки для const , но также может использоваться для перегрузки для volatile и const volatile . Это связано с тем, что все нестатические функции-члены принимают this как скрытый параметр, к которому применяются cv-квалификаторы. Это чаще всего используется для перегрузки для const , но также может использоваться для volatile и const volatile .

Это необходимо, потому что функция-член может быть вызвана только в том случае, если она, по меньшей мере, cv-квалифицирована как экземпляр, на который он вызван. В то время как экземпляр non 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 , а мутаторы - как const .


Никакие члены класса не могут быть изменены внутри функции const member. Если есть какой-то член, который вам действительно нужно изменить, например блокировку 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