1

This minimal compileable sample seems like a pretty standard setup of SFINAE:

#include <type_traits>

struct AType{};

// Helper type-trait templates for AType
template<typename T> struct isAType{ static const bool value = false; };
template<> struct isAType<AType>{ static const bool value = true; };

template<typename T>
void function( typename std::enable_if<isAType<T>::value, T>::type& t 
) {}

int main()
{
  AType a1;

  // This line, using automatic type deduction, fails to compile:  
  // function( a1 );

  // If you manually specify template parameter, it compiles:
  function<AType>( a1 );
}

The error message I get, when function( a1 ); is uncommented, is the following:

main.cpp: In function ‘int main()’:
main.cpp:17:16: error: no matching function for call to ‘function(AType&)’
   function( a1 );
                ^
main.cpp:10:6: note: candidate: template<class T> void function(typename 
std::enable_if<isAType<T>::value, T>::type&)
 void function( typename std::enable_if<isAType<T>::value, T>::type& t ) 
{}
      ^
main.cpp:10:6: note:   template argument deduction/substitution failed:
main.cpp:17:16: note:   couldn't deduce template parameter ‘T’
   function( a1 );

I've seen some posts indicating that "T" is in a nondeduced context. "Nondeduced context" is a new concept to me but enough ink has been spilled elsewhere that I can figure it out. My question here, I guess, is whether my declaration of function can be tweaked in such a way that automatic type deduction will succeed. Is there a canonical way to implement SFINAE with type traits such that automatic type deduction succeeds?

Michael Carilli
  • 371
  • 2
  • 12
  • 2
    Change it to `templateenable_if_t::value>> function(T) {}`. – David G Nov 23 '17 at 18:34
  • `template using isAType = std::is_same;`. – Jarod42 Nov 23 '17 at 20:00
  • Ah, neat, but that does require me to change the return type. – Michael Carilli Nov 23 '17 at 20:30
  • @MichaelCarilli What's the problem with that? The return type is still `void`. – David G Nov 23 '17 at 20:34
  • Oh yeah you're right, because std::enable_if::type defaults to void. This works fine: `template typename enable_if::value>::type function( T t ) {}` (same as your suggestion but using enable_if directly instead of enable_if_t). However, I can't get your version (with enable_if_t) to compile as you wrote it. I get the following error: `main.cpp:26:1: error: ‘enable_if_t’ does not name a type enable_if_t::value>, T> function(T) {} ` I am using namespace std and compiling with std=c++14. – Michael Carilli Nov 23 '17 at 22:47

1 Answers1

1

Not all C++ compilers support it, but if yours does this is the cleanest way to do this:

template<bool b>
using sfinae = typename std::enable_if< b, bool >::type;

template<class T,
  sfinae<isAType<T>::value> =true
>
void function( T& t  )
{
}

in I wouldn't bother with the sfinae alias, but getting rid of the typename makes it worth it in .

Note that the =true portion is required, but if it was =false it would mean the same thing.

What is going on here is we define a non-type template parameter whose type only exists if the test passes. We then give it a default value.

I find this technique reads the most like the incoming I mean I mean Concepts feature of C++.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks, this works. I guess the substitution checks can be performed using arguments, return type, or template parameters, and your answer is an example of the third approach. There is one thing that remains surprising to me: If I'm reading your code correctly, `sfinae::value>` is a type (bool) when isAType succeeds, so your code becomes equivalent to `template`. I checked, and writing `template` directly does compile. I am surprised that the compiler allows assigning a default value to a type like that without giving it a name. – Michael Carilli Nov 23 '17 at 23:15
  • Also, is there anything wrong with the following, which also uses a second dummy template parameter with default value? It compiles just fine: – Michael Carilli Nov 23 '17 at 23:22
  • `template::value>::type > void function( T t ) {}` – Michael Carilli Nov 23 '17 at 23:22
  • @michael it doesn't play nice with mutually exclusive SFINAE overloads as the default value is not part of the template signature; its existence is. – Yakk - Adam Nevraumont Nov 24 '17 at 00:07
  • I have heard this difference described as soft error vs hard error. The unnamed defaulted typename is a feature specifically to support a soft error: SFINAE - "is not an error" The named type must be resolved. It is a "proper" or "hard" error. – Gem Taylor Nov 24 '17 at 10:25