Я как раз использовал SFINAE для выбора шаблонов функций, когда мне пришла в голову великолепная идея инкапсулировать std::enable_if
в структуру, подобную этой.
template<typename T, typename U>
struct EnableWhenEqual
{
typedef typename std::enable_if<std::is_same<T, U>::value>::type type;
};
а потом использовать его, например, вот так
template<typename T, typename U, typename Enable = EnableWhenEqual<T,U>::type >
void foo(T&& t, U&& u)
{
if (std::is_same<T,U>::value)
std::cout << "OK, overload allowed." << std::endl;
else
std::cout << "Bad. Should not compile!" << std::endl;
}
Однако это не работает, как видно из вызова
foo(1,1); //prints "OK, overload allowed"
foo(1,2.0); //prints "Bad, should not compile", but obviously does
С другой стороны, пытаясь создать
EnableWhenEqual<int,int>(); //compiles
EnableWhenEqual<int,double>(); //does not compile because type does not exist
один получает ошибку компилятора ("тип не является членом std::enable_if").
В чем причина такого поведения? Я спрашиваю, потому что из моего небольшого знания SFINAE я бы подумал, что ошибка в выводе типа приводит к исключению перегрузки ...?
Для полноты изложенную выше проблему можно решить с помощью псевдонимов шаблонов.
template <typename T, typename U>
using EnableWhenEqual = typename std::enable_if<std::is_same<T,U>::value>::type;
Есть ли альтернатива использованию структуры вместо псевдонима шаблона?
EDIT: Здесь я имею в виду реализацию, которая решает общую проблему. Например вот это
template<typename T, typename U, bool B, typename C=void>
struct EnableWhenEqual {};
template<typename T, typename U, typename C>
struct EnableWhenEqual<T,U,std::is_same<T,U>::value>
{
typedef typename C type;
};
у меня не работает, потому что для частичной специализации нужен простой идентификатор. Если бы это было так, можно было бы заменить std::is_same
на общие структуры Condition<T,U>
. Любые альтернативы?
typename
перед аргументом по умолчаниюEnableWhenEqual
, я предполагаю, что вы используете какую-то версию VS старше 2013 года. Как только вы добавитеtypename
, ваш код не удается скомпилировать ни gcc, ни clang, ни VS2013 (error C2783: 'void foo(T &&,U &&)' : could not deduce template argument for 'Enable'
) - person Praetorian   schedule 02.09.2014EnableWhenEqual<T,U>::type
требует создания экземпляраEnableWhenEqual
, что приводит к созданию экземпляров всех объявлений членов. Член typedef иногда будет недопустимым, но эта ошибка не возникает в непосредственном контексте шаблона функцииfoo
(SFINAE == SFITICINAE = ошибка подстановки в непосредственном контексте не является ошибкой). - person dyp   schedule 02.09.2014enable_if
, то есть использовать специализацию шаблона:template<typename T, typename U> struct EnableWhenEqual {}; template<typename T> struct EnableWhenEqual<T,T> { using type = void; };
- person dyp   schedule 02.09.2014std::enable_if
иstd::enable_if_t
(С++ 14)? или псевдонимы шаблонов? Зачем вообще хотеть что-то вродеenable_if_same<>
? Его единственное преимущество в том, что он немного короче, чемstd::enable_if_t<std::is_same<>::value>
, но он вводит еще одну черту типа, которую нужно запомнить, хотя он тривиально создается из существующих. - person Walter   schedule 02.09.2014template <typename T, typename = std::enable_if<...>::type> void foo(T&& v) {...}
- person Oktalist   schedule 03.09.2014