Я активно использую функцию SFINAE в проекте и не уверен, есть ли какие-либо различия между следующими двумя подходами (кроме стиля):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
Вывод программы соответствует ожиданиям:
method 1
method 2
Done...
Я видел метод 2, который используется чаще в stackoverflow, но я предпочитаю метод 1.
Существуют ли обстоятельства, когда эти два подхода различаются?
Ответы:
Предложение: предпочитаю метод 2.
Оба метода работают с отдельными функциями. Проблема возникает, когда у вас больше одной функции с одной и той же сигнатурой, и вы хотите включить только одну функцию из набора.
Предположим, вы хотите включить
foo()
версию 1, когдаbar<T>()
(представьте, что этоconstexpr
функция)true
, иfoo()
версию 2, когдаbar<T>()
естьfalse
.С
вы получаете ошибку компиляции, потому что у вас есть неоднозначность: две
foo()
функции с одинаковой сигнатурой (параметр шаблона по умолчанию не меняет сигнатуру).Но следующее решение
работает, потому что SFINAE изменяет сигнатуру функций.
Несвязанное наблюдение: есть и третий метод: включить / отключить тип возвращаемого значения (за исключением конструкторов класса / структуры, очевидно)
Как и метод 2, метод 3 совместим с выбором альтернативных функций с одинаковой сигнатурой.
источник
foo()
функция остается доступной, когда вы вызываете ее с явным вторым параметром шаблона (foo<double, double>();
вызовом). И если остаются доступными, есть неоднозначность с другой версией. С помощью метода 2 SFINAE включает / отключает второй аргумент, а не параметр по умолчанию. Таким образом, вы не можете вызвать его, объясняя параметр, потому что произошла ошибка замещения, которая не позволяет второй параметр. Так что версия недоступна, поэтому нет двусмысленностиauto foo() -> std::enable_if_t<...>
часто полезен, чтобы избежать сокрытия сигнатуры функции и разрешить использование аргументов функции.В дополнение к ответу max66 , другая причина предпочтения метода 2 заключается в том, что с методом 1 вы можете (случайно) передать явный параметр типа в качестве второго аргумента шаблона и полностью отказаться от механизма SFINAE. Это может произойти как опечатка, ошибка копирования / вставки или недосмотр в более крупном шаблонном механизме.
Живая демо здесь
источник