Это определенно возможно и не нужно
-копию / ход,
-изменения в классах,
- изменение в теле функций (включая, помимо прочего, вызов другой статической функции шаблона класса ),
- изменить тип / значения возврата функции / с,
-или даже используя константное выражение в качестве типа возврата / s функции / s!
Решение
Просто необходимо иметь общую функцию в качестве шаблона функции, которая использует SFINAE метод в форме аргумента типа / s шаблона по умолчанию для дополнительного параметра (ов) типа шаблона, чтобы избежать особых случаев и иметь специализацию
как нормальная функция / с:
template <typename T, typename = std::enable_if_t<!std::is_base_of_v<A, T> > >
void foo(const T& t) {
printf("T");
}
void foo(const A& t) {
printf("A");
}
Объяснение:
Для общих случаев аргумент типа шаблона по умолчанию
std::enable_if_t<!std::is_base_of_v<A, T> > >
может быть выведен из первого аргумента типа шаблона T
. Поскольку он существует и он хорошо определен, будет вызван шаблон функции.
Когда функция вызывается с объектом типа, основанного на классе A
, поскольку std::enable_if_t<!std::is_base_of_v<A, T> > >
не определен, аргумент типа шаблона по умолчанию не существует, следовательно, параметр типа шаблона не может быть выведен. Таким образом, компилятор будет искать другие функции с тем же именем и схожим типом параметра, поэтому обычная функция
void foo(const A& t) { printf("A");}
будет называться, и не будет никакой двусмысленности.
Примечания по использованию
Для новой специализации просто добавьте еще один похожий псевдокласс в (один) шаблон функции и напишите функцию (не шаблон) для новой специализации.
Если аргумент типа шаблона по умолчанию выглядит большим и запутанным, можно просто создать шаблон политики и использовать его вместо этого. Как:
template<typename T, typename P>
using exclude = std::enable_if_t<!std::is_base_of_v<P, T> >;
template <typename T, typename = exclude<T,A> >
void foo(const T& t) {
printf("T");
}
Также, поскольку до C ++ 17 определенные функции не были включены, для более низких версий C ++ можно написать шаблон как:
template <typename T, typename = typename std::enable_if<!std::is_base_of<A, T>::value>::type>
Я добавляю, если кто-то решит использовать решение @songyuanyao, которое использует постоянное выражение в качестве типа возврата функций, если тип возвращаемых функций не void
, например return_type
,
Решение становится как:
template <typename T>
std::enable_if_t<!std::is_base_of_v<A, T>, return_type> foo(const T& t) {
printf("T");
return_type return_value;
return return_value;
}
template <typename T>
std::enable_if_t<std::is_base_of_v<A, T>, return_type> foo(const T& t) {
printf("A");
return_type return_value;
return return_value;
}
Дальнейший пример
Наконец, для лучшего понимания SFINAE Можно рассмотреть не совсем корректное / не всеохватывающее альтернативное решение, которое не требует какой-либо библиотеки:
template<bool>
struct ifnot;
template<>
struct ifnot<false> {
enum {v};
};
template<typename T, typename P>
struct test {
static T value_of_T();
static char check(...);
static int check(P);
enum {v = sizeof(check(value_of_T())) - 1};
};
template <typename T, bool = ifnot<test<T, A>::v>::v>
void foo(const T& t) {
printf("T");
}
void foo(const A& t) {
printf("A");
}
Хотя это решение также работает для этого конкретного примера, обратите внимание, что это решение не всегда корректно. Потому что он проверяет только преобразование T в A (что само по себе не является полным и проблематичным), а не наследование. Специально для такого рода функций, которые должны вызываться с объектами схожих типов, велика вероятность того, что многие из этих типов будут преобразованы друг в друга!
Я думаю, что правильный путь для теста наследования включает в себя тесты не взаимного преобразования и определение, если ни один из типов не равен void*
Все рассмотрено Гораздо лучше использовать std :: is_base_of или std :: is_base_of_v. Однако struct ifnot
в порядке, и можно даже обменять std::enable_if
на него с соответствующими изменениями в их использовании.
Удачи!