В книгах по C ++, почему нас учат использовать перегрузка функции когда у нас есть шаблоны в нашем распоряжении в C ++?
Не лучше ли показать эффективное (и правильное) использование шаблонов? В отличие от того, где перегрузка функций преподается в большинстве книг на C ++?
Или есть веские причины использовать один вместо другого?
Шаблоны дают преимущество, когда вы хотите выполнить одно и то же действие для типов, которые могут отличаться. Простой пример:
template <typename T>
T foo(const T& a, const T& b) { return a + b; }
Вы можете использовать перегрузку, когда хотите применить различные операции в зависимости от типа:
struct Foo{ void foo() const {} };
void foo(int i) { std::cout << "i = " << i << "\n"; }
void foo(const Foo& f) { f.foo(); }
Вы могли бы достичь вышеизложенного с помощью шаблонов и шаблонных специализаций, но такие специализации должны представлять несколько исключений из общего случая.
Шаблоны не могут принимать разное количество аргументов. Перегрузки могут. Кроме того, шаблон указывает, что вы можете работать с любым типом данных, но представлять это бессмысленно, хотя на самом деле подавляющее большинство шаблонов будут только специализациями (в вашей системе). Кроме того, перегрузки могут быть virtual
а шаблонные специализации не могут. Также специализации не могут отличаться своими подписями от базы.
template<typename T> void foo(T& t);
template<> void foo<std::string>(std::string* ptr); // Illegal
void foo(std::string* ptr); // Legal
Это сильно ограничит возможные виды перегрузки по сравнению с существующей системой.
Обычно вы используете шаблоны, когда хотите выполнить один и тот же набор операций для разных типов данных.
Обычно вы используете перегрузку функций, когда хотите выполнять различные операции с определенными наборами данных.
Преимущество шаблонов в ситуации, когда вы хотите выполнить один и тот же набор операций для множества различных типов данных, заключается в том, что компилятор будет обрабатывать для вас во время компиляции любой возможный новый тип, который вы можете создать в будущем, который использует шаблонную функцию. если бы вы использовали перегрузку функций, вам пришлось бы создавать новую перегрузку функции каждый раз, когда вы создавали новый тип, который вы хотели передать конкретной функции.
Просто дополнение к ответу Джанхопанзы:
С помощью перегрузок функций вы также можете изменять количество аргументов, что может быть удобно.
Простой пример, если у вас есть какая-то функция со следующим объявлением:
void foo(int i, int j);
Но вы часто вызываете foo с первым аргументом 0, вы можете написать следующую функцию, которая избавит вас от необходимости печатать.
void foo(int j) {
foo(0, j);
}
Шаблоны (обычно) требуют, чтобы вы использовали идентичные синтаксис выполнять операции на всех (поддерживаемых) типах.
Перегрузка функций (или должна быть) используется аналогично, но позволяет использовать разные синтаксис для выполнения операций для разных типов. То есть (хотя вам и не нужно) вы можете представлять значения по-разному. Одним очевидным примером будет то, что называется atan
а также atan2
в библиотеке C. С atan
, мы передаем отношение «подъем к« пробегу », и мы возвращаем угол, который представляет отношение. atan2
мы передаем значения для подъема и запускаем по отдельности (который вычисляет примерно тот же результат, но так как он дает немного больше входных данных, может также дать более полный результат).
Хотя они реализованы как совершенно отдельные функции в C, если бы они были написаны на C ++ с самого начала, было бы совершенно уместно использовать одно имя (например, atan
) перегружены как по одному, так и по двум параметрам:
double atan(double); // equivalent to current atan
double atan(double, double); // equivalent to current atan2
Шаблоны (если не считать специализации, которая в значительной степени просто переопределяет то, что предоставляют сами шаблоны), не предусматривает различий в вызове синтаксиса, подобного этому.
Перегрузка также более ограничена — вы предоставляете одну перегрузку для каждого конкретного типа, который вы хотите поддерживать (хотя, если вы берете указатель или ссылку, они также могут поддерживать производные типы). С шаблонами один шаблон может (по крайней мере потенциально) применяться к любой тип.