Функция принимающая в себя разные типы данных
Я новичок и только недавно начал изучать программирование поэтому прошу не бейте палками если я делаю что-то не так) Собственно в чём проблема: Я недавно проходил функции, прототипы функций, параметры функции по умолчанию, перегрузку функций и шаблоны функций. Во время изучения этих материалов у меня встал вопрос можно ли в c++ как-либо реализовать такую функцию которая работала бы с несколькими типами данных одновременно (в качестве примера чтобы ничего не усложнять в вопросе я взял простое сложение) Если реализовать через перегрузку:
#include <iostream>
using namespace std;
int foo(int a, int b)
{
return(a + b);
}
double foo(double a, double b)
{
return(a + b);
}
int main()
{
//setlocale(LC_ALL, "Rus");
cout << foo(1, 1) << endl;
cout << foo(1.5, 1.5) << endl;
cout << foo(1, 1.5) << endl; //не работает т.к. можно использовать сразу две функции
}
Можно конечно добавить больше перегрузок и сделать отдельные функции для int double и double int но мне кажется что это не правильно, громоздко и не удобно. Если реализовывать через шаблоны:
#include <iostream>
using namespace std;
template <typename T1, typename T2>
T1 foo(T1 = a, T2 = b)
{
return(a + b);
}
int main()
{
cout << foo(1, 1) << endl;
cout << foo(1.5, 1.5) << endl;
cout << foo(1, 1.5) << endl; //не работает правильно т.к. ответ приводится к типу первого аргумента
}
Ответы (1 шт):
В целом, сделать то, что хочется, немного сложно - ведь для разных типов будут разные способы сделать "сложение". Как упомянули в коментариях, сложить строку и число может быть немного затруднительно. Даже javascript решает задачу иногда оригинально странно, вместо просто показать ошибку. Но перейдем к нашей теме.
Когда типы одинаковые, то вопросов не возникает, а вот если типы разные, то нужен какой то один общий тип. И чудо, в стандартной библиотеке есть готовый функционал - common_type, где есть даже пример. Но я адаптирую Ваш
template <typename T1, typename T2>
typename std::common_type<T1, T2>::type foo(T1 a, T2 b)
{
return a + b;
}
И в этом случае, мы доверяем компилятору подобрать правильный тип.
Но у этого способа есть недостаток - он хорошо работает для арифметических типов, но плохо для каких-то пользовательских типов, для которых компилятор не может вывести общий тип. Но компилятор может его "угадать", если он может сложить типы. Самый простой способ - это просто написать auto и компилятор сам все сделает.
template <typename T1, typename T2>
auto foo(T1 a, T2 b)
{
return a + b;
}
Но если компилятор поддерживает только с++11, то этот способ не поможет, поэтому, можно подсказать компилятору:
template <typename T1, typename T2>
auto foo(T1 a, T2 b) -> decltype(a+b)
{
return a + b;
}
в этом случае мы просто говорим компилятору - посмотри на выражение a+b, выведи его тип самостоятельно и используй. Но это может понадобиться и для "новых крутых компиляторов". В некоторых случаях, если кол-во return больше одного, а типы в них получаются разные, то компилятор имеет право сказать "извени, я тут не могу" и указанный способ как раз и нужен - обычно программист лучше знает, что ему нужно.
Но у этого кода есть одна "бяка". Посмотрите на такой вызов
std::cout << foo("bar", "baz");
такой код не компилируется с кучей ошибок. Один из способов пофикисить - это дописать вот так
// не складывайте указатели на константные строки!!!
const char* foo(const char*, const char*) = delete;
теперь ошибка будет более явной.
Но все таки, тут есть способ написать нормальный оператор сложения, но это уже домашнее задание со звездочкой.