s4y
s4y

Reputation: 51685

Deduction fails on parameter, works on return value

I tried to use std::enable_if on a function parameter to trigger SFINAE. Compilation fails with this error:

type_nonsense.cpp:20:5: error: no matching function for call to 'c'
    c(SOME::VALUE);
    ^
type_nonsense.cpp:13:6: note: candidate template ignored: couldn't infer
      template argument 'T'
void c(typename std::enable_if<std::is_enum<T>::value, T>::type t) {}
     ^
1 error generated.

Moving the std::enable_if to either the return type or to a dummy template parameter works fine. Why?


#include <type_traits>

// Works
template <typename T, typename dummy = typename std::enable_if<std::is_enum<T>::value, T>::type>
void a(T t) {}

// Works
template <typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type b(T t) {}

// Fails to compile
template <typename T>
void c(typename std::enable_if<std::is_enum<T>::value, T>::type t) {}

enum class SOME { VALUE };

int main() {
    a(SOME::VALUE);
    b(SOME::VALUE);
    c(SOME::VALUE);
}

Upvotes: 2

Views: 230

Answers (2)

David G
David G

Reputation: 96790

A dependent type in a nested name specifier is a non-deduced context for template argument deduction, and as such cannot be used to determine the type of T. Placing the std::enable_if in either the return type or as a default template parameter works because the type of T isn't being deduced in those contexts.

If you need to place it as a parameter, you can do so like this:

template <typename T>
void c(T t, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr) {}

This works because T is being deduced by the first argument, not the second.

Upvotes: 5

Jarod42
Jarod42

Reputation: 217085

For the one which fails to compile, T is not deductible.

Upvotes: 1

Related Questions