Why does the following program not select the argument of the same type as the first template parameter?
Asked Answered
N

2

6

I am trying to write a function such that f<T>(args..) returns the first parameter of type T.

The following program seems to always select the first specialization thus printing 97 (ASCII code of 'a'). Though the second one wouldn't require converting char to int. Could someone please explain the behavior?

I am new to SFINAE and meta-programming.

  #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);
  }
Neary answered 20/4, 2019 at 16:30 Comment(1)
What do you actually intend the 2nd overload of f to do?Idiolect
C
4

Your code's first function parameter is in a non-deduced context. enable_if< expr, T >::type cannot deduce T. It is in a "non-deduced context".

Being unable to deduce T, foo<int>( 7 ) cannot use that overload; the compiler does not know what T is. foo<int,int>(7) would call it.

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

now T is in a deduced context. We aren't trying to deduce R (nor can we deduce from a return type).

Cascarilla answered 20/4, 2019 at 17:9 Comment(0)
C
8

The second template argument of the std::enable_if should be the R, which is what you desire to have.

Following should work

 template < typename R, typename T, typename ...Ts>
 typename enable_if<!is_same<R, T>::value, R>::type f(T const& t, Ts&&... args) 
 //                                       ^^^         ^^^^^^^^^^^
 {
       return f<R>(std::forward<Ts>(args)...); // forward the args further
 }
Coulter answered 20/4, 2019 at 16:48 Comment(2)
thanks for your answer. It'd help greatly if you'd explain how this code is different. It seems to me that both of them should work, since the goal is to just make one type dependent on is_same<R, T> so that when they are indeed same, the above specialization is not considered.Neary
@AbhishekKumar - the problem with your original code is that the type T in typename enable_if<!is_same<R, T>::value, T>::type a isn't deducible (T is in "not deducible context"; oversimplifying: is before the ::), so the recursive version is never enabled). This solution permit the T deduction from the a argument so SFINAE works as intended.Dagan
C
4

Your code's first function parameter is in a non-deduced context. enable_if< expr, T >::type cannot deduce T. It is in a "non-deduced context".

Being unable to deduce T, foo<int>( 7 ) cannot use that overload; the compiler does not know what T is. foo<int,int>(7) would call it.

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

now T is in a deduced context. We aren't trying to deduce R (nor can we deduce from a return type).

Cascarilla answered 20/4, 2019 at 17:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.