Как работает std:: enable_if?

Я просто задал этот вопрос: std:: numeric_limits как условие

Я понимаю, где std::enable_if будет определять возвращаемый тип метода, условно вызывающий компиляцию метода.

template<typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type foo(const T &bar) { isInt(bar); }

То, что я не понимаю, является вторым аргументом и кажущимся бессмысленным присваиванием std::enable_if, когда он объявлен как часть инструкции шаблона, как в Rapptz answer.

template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(const T& bar) { isInt(); }

Ответ 1

Как упоминается в комментарии 40two, понимание Ошибка замещения не является Ошибка является предпосылкой для понимания std::enable_if.

std::enable_if - это специализированный шаблон, определяемый как:

template<bool Cond, class T = void> struct enable_if {};
template<class T> struct enable_if<true, T> { typedef T type; };

Ключевым моментом здесь является то, что typedef T type определяется только тогда, когда bool Cond есть true.

Теперь, вооружившись этим пониманием std::enable_if, ясно, что void foo(const T &bar) { isInt(bar); } определяется:

template<typename T>
typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type foo(const T &bar) { isInt(bar); }

Как упоминалось firda, = 0 является дефолтом второго параметра шаблона. Причина по умолчанию в template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> заключается в том, что обе опции можно вызвать с помощью foo< int >( 1 );. Если параметр шаблона std::enable_if не был установлен по умолчанию, для вызова foo потребуется два параметра шаблона, а не только int.

В качестве примечания для пользователей версий Visual Studio до 2013 года вы не сможете использовать параметры шаблона по умолчанию, поэтому вы сможете использовать только первое определение: std:: numeric_limits как условие

Ответ 2

template<typename T, std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(const T& bar) { isInt(); }

это не скомпилируется, если T не является интегральным (поскольку enable_if<...>::type не будет определено). Это защита функции foo. Назначение = 0 есть для параметра шаблона по умолчанию, чтобы скрыть его.

Другая возможность: (да typename отсутствует в исходном вопросе)

#include <type_traits>

template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(const T& bar) {}

template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
bar(const T& foo) {}

int main() {
    foo(1); bar(1);
    foo("bad"); bar("bad");
}
error: no matching function for call to ‘foo(const char [4])’
  foo("bad"); bar("bad");
           ^
note: candidate is:
note: template::value, int>::type  > void foo(const T&)
 void foo(const T& bar) {}
      ^
note:   template argument deduction/substitution failed:
error: no type named ‘type’ in ‘struct std::enable_if’
 template::value, int>::type = 0>
                                                                                       ^
note: invalid template non-type parameter
error: no matching function for call to ‘bar(const char [4])’
  foo("bad"); bar("bad");
                       ^