Почему следующая программа не выбирает аргумент того же типа, что и первый параметр шаблона?

#c #templates #c 14 #variadic-templates #sfinae

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

Вопрос:

Я пытаюсь написать такую функцию, которая f<T>(args..) возвращает первый параметр типа T .

Следующая программа, похоже, всегда выбирает первую специализацию, таким образом печатая 97 (ASCII-код 'a' ). Хотя второй не требует преобразования char в int . Не мог бы кто-нибудь объяснить поведение?

Я новичок в SFINAE и мета-программировании.

   #include <iostream>
  using namespace std;

  template <typename T, typename ...Ts>
  T f(T a, Ts... args) {
    return a;
  }

  template <typename R, typename T, typename ...Ts>
  R f(typename enable_if<!is_same<R, T>::value, T>::type a, Ts... args) {
    return f<R>(args...);
  }

  int main() {
    cout << f<int>('a', 12);
  }
  

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

1. Что вы на самом деле собираетесь делать во 2-й перегрузке f ?

Ответ №1:

Вторым аргументом шаблона std::enable_if должен быть the R , который вы хотите иметь.

Следующее должно работать

  template < typename R, typename T, typename ...Ts>
 typename enable_if<!is_same<R, T>::value, R>::type f(T constamp; t, Tsamp;amp;... args) 
 //                                       ^^^         ^^^^^^^^^^^
 {
       return f<R>(std::forward<Ts>(args)...); // forward the args further
 }
  

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

1. спасибо за ваш ответ. Было бы очень полезно, если бы вы объяснили, чем отличается этот код. Мне кажется, что оба они должны работать, поскольку цель состоит в том, чтобы просто сделать один тип зависимым от is_same<R, T> , чтобы, когда они действительно одинаковы, вышеуказанная специализация не учитывалась.

2. @AbhishekKumar — проблема с вашим исходным кодом заключается в том, что тип T in typename enable_if<!is_same<R, T>::value, T>::type a не выводится ( T находится в «не выводимом контексте»; упрощение: находится перед :: ), поэтому рекурсивная версия никогда не включается). Это решение позволяет T вычитать из a аргумента, поэтому SFINAE работает так, как задумано.

Ответ №2:

Первый функциональный параметр вашего кода находится в неразведенном контексте. не enable_if< expr, T >::type удается вывести T . Он находится в «неразведенном контексте».

Будучи не в состоянии вывести T , foo<int>( 7 ) не может использовать эту перегрузку; компилятор не знает, что T такое. foo<int,int>(7) вызовет его.

   template <typename R, typename T, typename ...Ts>
  typename enable_if<!is_same<R, T>::value, R>::type f(T a, Ts... args) 
  

now T находится в выведенном контексте. Мы не пытаемся вывести R (и не можем вывести из возвращаемого типа).