Как сделать две реализации шаблонной функции при помощи SFINAE? C++
Есть функция, которая принимает лямбду. Я через SFINAE проверяю ее на возможность вызова с определенными типами аргументов. Если это невозможно, то шаблон отбрасывается:
template
<
typename Func,
typename = typename std::enable_if_t<std::is_invocable_v<Func, Pixel<PIXEL_TYPE>&, const int[]>>
>
void for_each(cv::Mat& img, Func&& callback) const {
// реализация
}
Все прекрасно работает, но я хочу сделать еще вторую реализацию функции, которая принимает лямбду с аргументом не Pixel<PIXEL_TYPE>, а просто PIXEL_TYPE. Я попытался сделать так:
template
<
typename Func,
typename = typename std::enable_if_t<std::is_invocable_v<Func, PIXEL_TYPE&, const int[]>>
>
void for_each(cv::Mat& img, Func&& callback) const {
// другая реализация
}
Естественно мне выбило ошибку, что функция уже объявлена. Как мне реализовать подобный механизм и возможно ли это вообще?
Ответы (1 шт):
Как вы заметили, шаблонные аргументы по умолчанию не учитываются при проверке функций на одинаковость.
Зато типы шаблонных параметров учитываются. Вместо typename = std::enable_if_t<...> используйте:
std::enable_if_t<..., std::nullptr_t> = nullptr
Также этот способ лучше тем, что проверку невозможно отключить, передав свой аргумент в этот параметр.
Тип здесь не обязан быть std::nullptr_t, можно взять какой-нибудь int. Но std::nullptr_t хорош тем, что у него только одно возможное значение, поэтому криворукий пользователь не сможет случайно инстанцировать ваш шаблон больше одного раза, передав разные аргументы в этот параметр.
В C++20 вместо SFINAE проще использовать requires:
template <typename Func> requires std::is_invocable_v<...>
Или, еще лучше, концепт std::invocable:
template <std::invocable<Pixel<PIXEL_TYPE>&, const int[]> Func>