Функции и операции
Функциям, выполняющим одинаковые действия с данными различных типов, в С++ можно дать одинаковые имена. Компилятор сможет определить, какую функцию из
набора вызвать, по типу аргументов.
void print(int x)
{ printf("%d",x);
}
void print(char x)
{ printf("%c",x);
}
int main()
{
print(10);
print('A');
}
Чтобы на этапе компоновки программы можно было установить соответствие между функциями и их вызовами в имя функции на языке С++ включаются типы ее параметров. Для функций на языке С типы параметров не важны, поэтому перед объявлением
функции из библиотек на языке С нужно написать
extern "C".
Обычно один и тот же заголовочный файл используется для программ на С и С++, поэтому в него добавляет следующие строки:
#ifdef __cplusplus
extern "C" {
#endif
//
#ifdef __cplusplus
}
#endif
Вместо определения набора функций, отличающихся только количеством параметров, можно указать при объявлении функции в заголовочном файле
значения по умолчанию для некоторых параметров. Эти параметры должны быть последними в списке. Если при вызове соответствующий аргумент пропущен, то должны быть пропущены и все аргументы за ним. В качестве значений по умолчанию можно использовать константы и глобальные переменные.
void f(int a, int n=100, double eps=1e-9);
...
f(1,200,1e-6);
f(1,200); //
f(1); //
В C++ можно переопределять операции для собственных типов данных и, также как для функций, нужный вариант операции будет вызван в зависимости от типов аргументов. Разработчиками языка эта возможность была использована, чтобы избавиться от проблемы несоответствия форматов и списка аргументов в функциях форматированного ввода-вывода. При изменении типа переменной в программе на С необходимо было отследить все появления этой переменной в списке аргументов в
printf/scanf и изменить строки с форматами.
int a,b;
scanf("%d%d",&a,&b);
В С++ для ввода-вывода были переопределены операции
<< и
>> (в С это операции поразрядного сдвига для целых чисел). Первым аргументом операции указывается
поток ввода (например, cin – консольный ввод, console input) или поток вывода (например, cout – консольный вывод, console output), а вторым аргументом – вводимая переменная или выводимое значение. В качестве результата операция возвращает поток и поэтому её можно применить последовательно для ввода или вывода нескольких значений.
int a,b;
cin>>a>>b;
cout<<"a="<<a<<" b="<<b<<"\n";
Обратите внимание на отсутствие форматов и операции &, так как в операции ввода происходит передача аргумента по ссылке.
Запомнить: направление уголков указывает направление передачи информации.
Лямбда-выражения и анонимные функции можно использовать везде, где требуется передавать в качестве аргумента функцию.
Лямбда-выражения создаются следующим образом:
[замыкание](параметры){ return выражение; }
Тип лямбда-выражения определяется по типу возвращаемого значения.
vector<int> v;
//
//
int gt(int x){ return x>0; }
...
int n=count_if(v.begin(),v.end(),gt); //
//
int n=count_if(v.begin(),v.end(),[](int x){ return x>0; });
Анонимные функции создаются аналогичным образом, но нужно указать тип результата и можно писать любые операторы в теле функции:
[замыкание](параметры) –> тип_результата { операторы }
В
замыкании нужно указать список имен объектов, которые используются для вычислений, но не передаются как параметры. Перед именем объекта можно указать &, если объект нужно передавать по ссылке, иначе в замыкании будет копия значения объекта в момент определения лямбда-выражения. Можно не перечислять самостоятельно объекты поименно, а указать
[=], если все объекты должны храниться по значению, или
[&] для передачи по ссылке. Важно: лямбда-выражение, использующее в замыкании ссылки на локальные объекты блока, становится ошибочным после завершения блока, так как объекты будут уничтожены. При использовании в замыкании копий значений, лямбда-выражение останется корректным. Можно поместить в замыкание любое значение и дать ему имя.
int i=0;
//
generate(v.begin(),v.end(),[&i]()->int { ++i; return i*i; });
auto one = [value=1](){return value;};
cout<<one(); //
Альтернативный синтаксис можно использовать при определении обычных функций. Такой синтаксис особенно полезен для шаблонов функций, когда тип результата определить затруднительно.
auto f(int x) -> int;
template <typename T1, typename T2>
auto add(const T1 &a, const T2 &b) -> decltype(a+b)
{ return a+b;
}