I just asked this question: std::numeric_limits as a Condition
I understand the usage where std::enable_if
will define the return type of a method conditionally causing the method to fail to compile.
template<typename T> typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type foo(const T &bar) { isInt(bar); }
What I don't understand is the second argument and the seemingly meaningless assignment to std::enable_if
when it's declared as part of the template statement, as in Rapptz answer.
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> void foo(const T& bar) { isInt(); }
std::enable_if is a convenient utility to use boolean conditions to trigger SFINAE.
" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.
As is mentioned in comment by 40two, understanding of Substitution Failure Is Not An Error is a prerequisite for understanding std::enable_if
.
std::enable_if
is a specialized template defined as:
template<bool Cond, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; };
The key here is in the fact that typedef T type
is only defined when bool Cond
is true
.
Now armed with that understanding of std::enable_if
it's clear that void foo(const T &bar) { isInt(bar); }
is defined by:
template<typename T> typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type foo(const T &bar) { isInt(bar); }
As mentioned in firda's answer, the = 0
is a defaulting of the second template parameter. The reason for the defaulting in template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
is so that both options can be called with foo< int >( 1 );
. If the std::enable_if
template parameter was not defaulted, calling foo
would require two template parameters, not just the int
.
General note, this answer is made clearer by explicitly typing out typename std::enable_if<std::numeric_limits<T>::is_integer, void>::type
but void
is the default second parameter to std::enable_if
, and if you have c++14 enable_if_t
is a defined type and should be used. So the return type should condense to: std::enable_if_t<std::numeric_limits<T>::is_integer>
A special note for users of visual-studio prior to visual-studio-2013: Default template parameters aren't supported, so you'll only be able to use the enable_if
on the function return: std::numeric_limits as a Condition
template<typename T, std::enable_if<std::is_integral<T>::value, int>::type = 0> void foo(const T& bar) { isInt(); }
this fails to compile if T
is not integral (because enable_if<...>::type
won't be defined). It is protection of the function foo
.The assignment = 0
is there for default template parameter to hide it.
Another possibility: (yes the typename
is missing in original question)
#include <type_traits> template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> void foo(const T& bar) {} template<typename T> typename std::enable_if<std::is_integral<T>::value>::type bar(const T& foo) {} int main() { foo(1); bar(1); foo("bad"); bar("bad"); }
error: no matching function for call to ‘foo(const char [4])’ foo("bad"); bar("bad"); ^ note: candidate is: note: template::value, int>::type > void foo(const T&) void foo(const T& bar) {} ^ note: template argument deduction/substitution failed: error: no type named ‘type’ in ‘struct std::enable_if’ template::value, int>::type = 0> ^ note: invalid template non-type parameter error: no matching function for call to ‘bar(const char [4])’ foo("bad"); bar("bad"); ^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With