I try to use std::enable_if
with an unused and unnamed type parameter, in order to not distort the return
type. However, the following code does not compile.
#include <iostream>
template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
The compiler says:
7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:
What is the problem here? How do I have to change the code to get it compile? The text book "Discovering Modern C++" explicitly encourages the use of std::enable_if
with anonymous type parameters.
EDIT: I know that it works if I put std::enable_if
into the return type. However, my intention is to get some more details why it does not work if I use it with anonymous type parameters. As I said, my text book encourages the variant using anonymous type parameters, so I am wondering why my code does not compile.
There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.
Default values do not participate in overload resolution, thus you are actually redefining the same function.
Let's simplify your example:
The code above does not compile, for it couldn't know what version of
f
you want to invoke.In your case, if I invoke
foo
asfoo<void, void>
, I've almost the same problem.The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.
Because of that, the code is ill-formed and the compiler correctly gives you an error.
As a side note, you can still have it working without using the
std::enable_if_t
in the return type.As an example:
While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, @T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:
You can put
enable_if
into the return type:By the way,
enable_if_t
is available from C++14, so you might want to saytypename std::enable_if<std::is_integral<T>::value, T>::type
instead. Quite a mouthful.But a little more idiomatic (and readable) would be to dispatch basing on the type:
Your error is that you're using
enable_if_t
on the right of the equal sign.You have to use it on the left
But this work with C++14.
In C++11 (your question is tagged C++11) you don't have
enable_if_t
.The code become