C++ SFINAE enable_if_t in member function, how to disambiguate?
C++ SFINAE enable_if_t in member function, how to disambiguate?
Suppose we have some SFINAE member function:
class foo
template <class S, class = std::enable_if_t<std::is_integral<S>::value, S>
void bar(S&& s);
template <class S, class = std::enable_if_t<!std::is_integral<S>::value, S>
void bar(S&& s);
If we declared it as above, then how can we define them? Both of their function signatures would look like:
template <class S, class>
inline void foo::bar(S&& s) ... do something ...
I have seen examples where one returns an std::enable_if_t<...>
like:
std::enable_if_t<...>
template <class S, class>
auto bar(S&& s) -> std::enable_if_t<!std::is_integral<S>::value, S>(...)
... do something ...
To disambiguate based off of the return type. But I don't want to return anything.
Related: stackoverflow.com/q/51292574/1896169
– Justin
Aug 29 at 18:29
3 Answers
3
since default arguments are not part of a function signature, make them not default
class foo
template <class S, typename std::enable_if<std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
template <class S, typename std::enable_if<!std::is_integral<S>::value, int>::type = 0>
void bar(S&& s);
;
EDIT: by popular demand, Here's the same code in C++17:
class foo
public:
template <class S>
void bar(S&& s)
if constexpr(std::is_integral_v<S>)
std::cout << "is integraln";
else
std::cout << "NOT integraln";
;
constexpr if statements are special to the compiler because the branch is chosen at compile time, and the non-taken branch isn't even instantiated
C++17 Demo
Ah, and I would need to add the same
typename std::enable_if<...>
in the definition as well, got it.– OneRaynyDay
Aug 29 at 18:24
typename std::enable_if<...>
Yeah, the gist is not to use
class =
syntax, but rather use an explicit type. Preferably an integral one so that you can default its value (like we did with int
and 0
)– AndyG
Aug 29 at 18:25
class =
int
0
My approach is
typename<class S, std::enable_if_t<condition>* = nullptr>
– milleniumbug
Aug 29 at 18:27
typename<class S, std::enable_if_t<condition>* = nullptr>
@milleniumbug: Whether that is defined behavior or not is up for debate (
void*
is not a pointer to a class type) Let me find the issue page for that.– AndyG
Aug 29 at 18:28
void*
I see - so why do we need to set = 0 or = nullptr? I've seen usages where you didn't need to do that. Also, could you add the constexpr answer? I'd love to learn about it. I am using c++11 in this case, but still. I have since upvoted and will accept your answer, thanks
– OneRaynyDay
Aug 29 at 18:28
You can still do this in the return type just fine. Just keep the default of enable_if
(which is void
). Even if you're just on C++11, just add this alias:
enable_if
void
template <bool B, typename T=void>
using enable_if_t = typename std::enable_if<B, T>::type;
And then you can do:
template <class S>
enable_if_t<std::is_integral<S>::value>
bar(S);
template <class S>
enable_if_t<!std::is_integral<S>::value>
bar(S);
Or:
template <class S>
auto bar(S) -> enable_if_t<std::is_integral<S>::value>
template <class S>
auto bar(S) -> enable_if_t<!std::is_integral<S>::value>
Either way, you have two properly disambiguated functions that return void
.
void
Ah, that's clever. Thanks :)
– OneRaynyDay
Aug 29 at 18:34
What is the point of redefining
std::enable_if_t
which already has T = void
as a default argument? Your code compiles with std::enable_if_t
just fine (using C++14). Maybe you wanted to write template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
to make it C++11?– Evg
Aug 29 at 18:59
std::enable_if_t
T = void
std::enable_if_t
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
@Evgeny Yeah that's what I meant. Thanks!
– Barry
Aug 29 at 19:00
With C++11 compiler another option is to use tag dispatching.
template <class S>
void bar(S&& s)
bar(std::forward<S>(s), std::is_integral<S>);
template <class S>
void bar(S&& s, std::true_type)
...
template <class S>
void bar(S&& s, std::false_type)
...
Ah, thank you, I haven't yet familiarized myself with constexpr. Since I am using c++11 I'll have to accept Andy's answer, but I have upvoted yours
– OneRaynyDay
Aug 29 at 18:31
@OneRaynyDay, Andy has updated his answer, so I removed
constexpr
solution and added another one for C++11.– Evg
Aug 29 at 18:43
constexpr
@Evgeny: Love tagged dispatch +1. You can just pass std::is_integral<S> directly to
bar
without needing to wrap it with an integral_constant
Demo– AndyG
Aug 29 at 19:14
bar
integral_constant
@AndyG, very good remark, thanks.
std::is_integral
inherits from std::integral_constant
. Corrected the answer.– Evg
Aug 29 at 19:18
std::is_integral
std::integral_constant
@Evgeny: Precisely. Possible object slicing? Definitely! Do we care? Heck no!
– AndyG
Aug 29 at 19:20
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
It doesn't look like you're actually using C++11. If you're actually using C++17 we can make things easier with a constexpr if statement
– AndyG
Aug 29 at 18:27