I am using function SFINAE heavily in a project and am not sure if there are any differences between the following two approaches (other than style):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
The program output is as expected:
method 1
method 2
Done...
I have seen method 2 used more often in stackoverflow, but I prefer method 1.
Are there any circumstances when these two approaches differ?
I have seen method 2 used more often in stackoverflow, but I prefer method 1.
Suggestion: prefer method 2.
Both methods work with single functions. The problem arises when you have more than a function, with the same signature, and you want enable only one function of the set.
Suppose that you want enable foo()
, version 1, when bar<T>()
(pretend it's a constexpr
function) is true
, and foo()
, version 2, when bar<T>()
is false
.
With
template <typename T, typename = std::enable_if_t<true == bar<T>()>>
void foo () // version 1
{ }
template <typename T, typename = std::enable_if_t<false == bar<T>()>>
void foo () // version 2
{ }
you get a compilation error because you have an ambiguity: two foo()
functions with the same signature (a default template parameter doesn't change the signature).
But the following solution
template <typename T, std::enable_if_t<true == bar<T>(), bool> = true>
void foo () // version 1
{ }
template <typename T, std::enable_if_t<false == bar<T>(), bool> = true>
void foo () // version 2
{ }
works, because SFINAE modify the signature of the functions.
Unrelated observation: there is also a third method: enable/disable the return type (except for class/struct constructors, obviously)
template <typename T>
std::enable_if_t<true == bar<T>()> foo () // version 1
{ }
template <typename T>
std::enable_if_t<false == bar<T>()> foo () // version 2
{ }
As method 2, method 3 is compatible with selection of alternative functions with same signature.