Разница между двумя шаблонами кода шаблона, где в одном случае присваивается номер, а в другом используется ключевое слово typename

#c #templates #c 14 #sfinae

#c #шаблоны #c 14 #sfinae

Вопрос:

В следующем коде, в чем разница между следующими двумя строками шаблона.

 >      1. template<class T, std::enable_if_t<std::is_integral<T>::value, int> = 0> 
>      2. template<class T, typename = std::enable_if_t<std::is_integral<T>::value>>
  

Обе вышеуказанные строки работают нормально, я просто хотел узнать преимущества / недостатки использования одного над другим.

 #include <type_traits>
#include <iostream>
template<class T, std::enable_if_t<std::is_integral<T>::value, int> = 0>

//template<class T, typename = std::enable_if_t<std::is_integral<T>::value>>


int onlyOnInt(T a, T b)
{
    return a b;
}
int main()
{
    onlyOnInt(1, 2);
}
  

Ответ №1:

Они оба работают нормально, если вы пишете одну функцию.

Но если вам нужны две альтернативные функции, этот способ

 template <typename T, typename = std::enable_if_t<true == std::is_integral_v<T>>>
void foo (T const amp;)
 { std::cout << "is integral" << std::endl; }

template <typename T, typename = std::enable_if_t<false == std::is_integral_v<T>>>
void foo (T const amp;)
 { std::cout << "isn't integral" << std::endl; }
  

вы получаете ошибку компиляции, когда таким образом

 template <typename T, std::enable_if_t<true == std::is_integral_v<T>, int> = 0>
void foo (T const amp;)
 { std::cout << "is integral" << std::endl; }

template <typename T, std::enable_if_t<false == std::is_integral_v<T>, int> = 0>
void foo (T const amp;)
 { std::cout << "isn't integral" << std::endl; }
  

работает.

Причина?

Считайте, что вы играете с SFINAE, то есть ошибка замены не является ошибкой.

Суть в подстановке.

Первый способ, когда вы вызываете

 foo(0)
  

замена приводит к

 template <typename T, typename = void>
void foo (T const amp;)
 { std::cout << "is integral" << std::endl; }

template <typename T, typename>
void foo (T const amp;)
 { std::cout << "isn't integral" << std::endl; }
  

то есть… у вас есть две функции с одинаковыми сигнатурами (аргумент шаблона по умолчанию не изменяет сигнатуру функции) и коллизия, вызывающая ее.

Во втором случае у вас есть только

 template <typename T, int = 0>
void foo (T const amp;)
 { std::cout << "is integral" << std::endl; }
  

поскольку ошибка замены во второй функции делает функцию непригодной для использования, и она отбрасывается. Таким образом, у вас есть только доступная функция и нет коллизии.

Комментарии:

1. Четкое объяснение