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.





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





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.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)