`sfinae` proposal… useful or unnecessary?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












10














I am trying to learn what SFINAE is. I believe I understand it quite well; however, I do not have a lot of practice in the subject. I mean I would find it difficult to list all the cases in which SFINAE should or could be used, or it would be difficult for me to immediately understand what a given sample of an SFINAE code should do. However, I believe I understand the core concept well enough to use it effectively.



Nonetheless looking through examples of SFINAE usage I came to the few conclusions.



One of which is: it is extremely pointless to use a chain of std::enable_if_t, std::void_t, std::is_same, etc, etc, etc, to define a single type using type = int *. IMHO (of a "newbie" with untrained eyes) such chains make the code less (extremely less) readable.



The second conclusion was: wouldn't it be useful to have the ability to check few conditions at the same time?



So I created this alias: (well "created" is waaay too big word... I just wrote 4 lines of code...)



template<typename T, typename ...>
using sfinae = T;

template<typename T, typename ...>
using SFINAE = T;


Usage examples



// 1) within class declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;

// 2) within function declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
void foo;

// 3) as a function return type:
template <typename T>
sfinae<T,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> const & foo(T const & val);

// 4) To define other SFINAE-conditions as aliases:
template <typename T>
using check_xyz = sfinae<void,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
>;

template <typename T, typename = check_xyz<T>>
class foo;


And my main concern/question is: would a professional c++ developer deem sfinae</*...*/> (as I proposed it) useful or extremely unnecessary? Are there any changes that could be introduced to the idea?










share|improve this question























  • Please turn all ... into actual code. We review only complete snippets.
    – t3chb0t
    Dec 19 '18 at 17:43






  • 7




    @t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
    – cukier9a7b5
    Dec 19 '18 at 17:47











  • Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
    – Mast
    Dec 19 '18 at 17:48










  • I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
    – t3chb0t
    Dec 19 '18 at 17:50






  • 1




    I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
    – t3chb0t
    Dec 19 '18 at 17:52















10














I am trying to learn what SFINAE is. I believe I understand it quite well; however, I do not have a lot of practice in the subject. I mean I would find it difficult to list all the cases in which SFINAE should or could be used, or it would be difficult for me to immediately understand what a given sample of an SFINAE code should do. However, I believe I understand the core concept well enough to use it effectively.



Nonetheless looking through examples of SFINAE usage I came to the few conclusions.



One of which is: it is extremely pointless to use a chain of std::enable_if_t, std::void_t, std::is_same, etc, etc, etc, to define a single type using type = int *. IMHO (of a "newbie" with untrained eyes) such chains make the code less (extremely less) readable.



The second conclusion was: wouldn't it be useful to have the ability to check few conditions at the same time?



So I created this alias: (well "created" is waaay too big word... I just wrote 4 lines of code...)



template<typename T, typename ...>
using sfinae = T;

template<typename T, typename ...>
using SFINAE = T;


Usage examples



// 1) within class declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;

// 2) within function declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
void foo;

// 3) as a function return type:
template <typename T>
sfinae<T,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> const & foo(T const & val);

// 4) To define other SFINAE-conditions as aliases:
template <typename T>
using check_xyz = sfinae<void,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
>;

template <typename T, typename = check_xyz<T>>
class foo;


And my main concern/question is: would a professional c++ developer deem sfinae</*...*/> (as I proposed it) useful or extremely unnecessary? Are there any changes that could be introduced to the idea?










share|improve this question























  • Please turn all ... into actual code. We review only complete snippets.
    – t3chb0t
    Dec 19 '18 at 17:43






  • 7




    @t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
    – cukier9a7b5
    Dec 19 '18 at 17:47











  • Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
    – Mast
    Dec 19 '18 at 17:48










  • I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
    – t3chb0t
    Dec 19 '18 at 17:50






  • 1




    I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
    – t3chb0t
    Dec 19 '18 at 17:52













10












10








10


2





I am trying to learn what SFINAE is. I believe I understand it quite well; however, I do not have a lot of practice in the subject. I mean I would find it difficult to list all the cases in which SFINAE should or could be used, or it would be difficult for me to immediately understand what a given sample of an SFINAE code should do. However, I believe I understand the core concept well enough to use it effectively.



Nonetheless looking through examples of SFINAE usage I came to the few conclusions.



One of which is: it is extremely pointless to use a chain of std::enable_if_t, std::void_t, std::is_same, etc, etc, etc, to define a single type using type = int *. IMHO (of a "newbie" with untrained eyes) such chains make the code less (extremely less) readable.



The second conclusion was: wouldn't it be useful to have the ability to check few conditions at the same time?



So I created this alias: (well "created" is waaay too big word... I just wrote 4 lines of code...)



template<typename T, typename ...>
using sfinae = T;

template<typename T, typename ...>
using SFINAE = T;


Usage examples



// 1) within class declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;

// 2) within function declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
void foo;

// 3) as a function return type:
template <typename T>
sfinae<T,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> const & foo(T const & val);

// 4) To define other SFINAE-conditions as aliases:
template <typename T>
using check_xyz = sfinae<void,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
>;

template <typename T, typename = check_xyz<T>>
class foo;


And my main concern/question is: would a professional c++ developer deem sfinae</*...*/> (as I proposed it) useful or extremely unnecessary? Are there any changes that could be introduced to the idea?










share|improve this question















I am trying to learn what SFINAE is. I believe I understand it quite well; however, I do not have a lot of practice in the subject. I mean I would find it difficult to list all the cases in which SFINAE should or could be used, or it would be difficult for me to immediately understand what a given sample of an SFINAE code should do. However, I believe I understand the core concept well enough to use it effectively.



Nonetheless looking through examples of SFINAE usage I came to the few conclusions.



One of which is: it is extremely pointless to use a chain of std::enable_if_t, std::void_t, std::is_same, etc, etc, etc, to define a single type using type = int *. IMHO (of a "newbie" with untrained eyes) such chains make the code less (extremely less) readable.



The second conclusion was: wouldn't it be useful to have the ability to check few conditions at the same time?



So I created this alias: (well "created" is waaay too big word... I just wrote 4 lines of code...)



template<typename T, typename ...>
using sfinae = T;

template<typename T, typename ...>
using SFINAE = T;


Usage examples



// 1) within class declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;

// 2) within function declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
void foo;

// 3) as a function return type:
template <typename T>
sfinae<T,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> const & foo(T const & val);

// 4) To define other SFINAE-conditions as aliases:
template <typename T>
using check_xyz = sfinae<void,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
>;

template <typename T, typename = check_xyz<T>>
class foo;


And my main concern/question is: would a professional c++ developer deem sfinae</*...*/> (as I proposed it) useful or extremely unnecessary? Are there any changes that could be introduced to the idea?







c++ sfinae






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 21 '18 at 18:59

























asked Dec 19 '18 at 17:31









cukier9a7b5

22817




22817











  • Please turn all ... into actual code. We review only complete snippets.
    – t3chb0t
    Dec 19 '18 at 17:43






  • 7




    @t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
    – cukier9a7b5
    Dec 19 '18 at 17:47











  • Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
    – Mast
    Dec 19 '18 at 17:48










  • I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
    – t3chb0t
    Dec 19 '18 at 17:50






  • 1




    I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
    – t3chb0t
    Dec 19 '18 at 17:52
















  • Please turn all ... into actual code. We review only complete snippets.
    – t3chb0t
    Dec 19 '18 at 17:43






  • 7




    @t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
    – cukier9a7b5
    Dec 19 '18 at 17:47











  • Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
    – Mast
    Dec 19 '18 at 17:48










  • I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
    – t3chb0t
    Dec 19 '18 at 17:50






  • 1




    I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
    – t3chb0t
    Dec 19 '18 at 17:52















Please turn all ... into actual code. We review only complete snippets.
– t3chb0t
Dec 19 '18 at 17:43




Please turn all ... into actual code. We review only complete snippets.
– t3chb0t
Dec 19 '18 at 17:43




7




7




@t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
– cukier9a7b5
Dec 19 '18 at 17:47





@t3chb0t, but the actual code is template<typename T, typename ...> using sfinae = T; and template<typename T, typename ...> using SFINAE = T; everything else is just a usage examples... Also because of the keywords "immediate context" (which can be found in cpp draw, the sfinae section. Usage of this aliases is limited declarations only. However... I could delete definitions (it would by erase all ... by an extension).
– cukier9a7b5
Dec 19 '18 at 17:47













Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
– Mast
Dec 19 '18 at 17:48




Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site. Please take a look at the help center.
– Mast
Dec 19 '18 at 17:48












I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
– t3chb0t
Dec 19 '18 at 17:50




I know, inside templates yes, but there are many ... elsewhere and I now admit that I didn't see the // usage examples: comment. I'd be better if it was a heading so that it's better visible... let me edit this...
– t3chb0t
Dec 19 '18 at 17:50




1




1




I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
– t3chb0t
Dec 19 '18 at 17:52




I think your quesiton is fine after all. Not much code but the rest is just examples that weren't easy to notice. I'll retract my close-vote now... @Mast?
– t3chb0t
Dec 19 '18 at 17:52










3 Answers
3






active

oldest

votes


















14















I just wrote four lines of code...



template<typename T, typename ...>
using sfinae = T;

template<typename T, typename ...>
using SFINAE = T;



You should have written just two lines of code — either the first two or the last two. Not both. "There's more than one way to [spell] it" is the Perl way, not the C++ way. ;) (At least not intentionally! Not intentionally pre-C++2a! Now we're getting both std::filesystem and std::fs, that's a less airtight position.)




As you show, your thing can be used for



template<class T, 
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true>
void foo();


However, this is pretty verbose; surely we'd rather write std::enable_if_t<std::is_integral_v<T> && std::is_arithmetic_v<T>> than std::enable_if_t<std::is_integral_v<T>>, std::enable_if_t<std::is_arithmetic_v<T>>.



So personally I have a different alias that accomplishes the same goal: bool_if_t.



template<bool B>
using bool_if_t = std::enable_if_t<B, bool>;

template<class T,
bool_if_t<
std::is_integral_v<T> &&
std::is_arithmetic_v<T>
> = true>
void foo();


This has the advantage that the inner expression is written using normal C++ syntax rules. We don't have to remember some arbitrary rule like "x, y means both x and y must be true"; we just write x && y. And if we want to enable this function when either x or y is true, we don't have to invent a new primitive; we just write bool_if_t<x || y>. bool_if_t is much more composable than your sfinae<Ts...>, because bool_if_t can exploit the existing expression syntax of C++.




Recommended viewing: "A Soupçon of SFINAE" (Arthur O'Dwyer, CppCon 2017). (Yes, that's me.)



You picked examples that don't really show off the power of sfinae, by the way.
Consider this example:



// 3) as a function return type:
template <typename T>
sfinae<T,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> const & foo(T const & val);


You take a "value-space" boolean (is_integral_v<T>), lift it up into "SFINAE-space" with enable_if_t, and then apply a logical AND operation in SFINAE-space using your sfinae alias.



template <typename T> 
std::enable_if_t<
std::is_integral_v<T> &&
std::is_arithmetic_v<T>, T
> const & foo2(T const & val);


Here I take the same "value-space" boolean, perform the logical AND in value space where we have a dedicated operator && for maximum expressiveness; and I lift it up into SFINAE-space after doing the AND. This is clearer and also probably more efficient in terms of compile time.



Where your version helps is when your values start in SFINAE-space!



template<class T> 
sfinae<T,
decltype(std::declval<T>() + std::declval<T>()),
T*,
T&
> const & foo3(T const & val);


If we were trying to replicate this code's behavior with the standard tools, we'd say something like



template<class, class=void> struct has_plus : std::false_type ;
template<class T> struct has_plus<T, decltype(void(std::declval<T>() + std::declval<T>()))> : std::true_type ;
template<class T> inline constexpr bool has_plus_v = has_plus<T>::value;

template<class T>
std::enable_if_t<
has_plus_v<T> &&
not std::is_reference_v<T> &&
not std::is_void_v<T>, T
> const & foo4(T const & val);


That is, we start with a value in SFINAE-space (decltype(std::declval<T>() + std::declval<T>())), lift it into value-space (has_plus_v<T>), do the logical AND in value-space, and lift the result back into SFINAE-space.



Whereas with your foo3, you start in SFINAE-space, do the logical AND via sfinae<...> without leaving SFINAE-space, and then you're done. Much simpler! But harder to read, I think.




A simple way to improve readability is to pick a good name. sfinae_space_and<Ts...> might be clearer. Can you think of a way to write sfinae_space_or<Ts...>? How about sfinae_space_not<T>? Does it make sense, maintainability-wise, to provide one without the others?






share|improve this answer
















  • 5




    Incredible how much one can write about four (two) lines of code. Very informative ;-)
    – t3chb0t
    Dec 19 '18 at 18:42










  • What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
    – Flamefire
    Dec 20 '18 at 11:56










  • @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
    – Quuxplusone
    Dec 20 '18 at 16:28










  • Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
    – Quuxplusone
    Dec 20 '18 at 16:30






  • 1




    @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
    – Quuxplusone
    Dec 21 '18 at 22:23


















9














A bit dated...



First of all, let me say that since C++20 much of SFINAE will be unnecessary. Concepts have SFINAE enabled by default, have much nicer syntax, and have greater reuse capabilities.



Code Review



Personally I don't see much improvement over original one. What I would prefer instead of



template <typename T, typename ... >
...


is



template <typename T, bool ... Conditionals>
... //perform folding


Even then, people sometimes have non-trivial logic like AND-ing and OR-ing the results of conditionals.



Much of the complexity lies in that conditional statements, which could be moved away by using something like template variable:



template <typename T>
constexpr inline bool supports_xxx = /*is_same, trivial, etc*/;


and then people could just use



template <typename T, typename = std::enable_if_t<supports_xxx<T>>>
...



Example



// 1) within class declaration:
template <typename T,
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;


is better written as



template <typename T>
constexpr inline bool is_int_arithmetic_v = std::is_integral_v<T>
&& std::is_arithmetic_v<T>;

template <typename T,
typename = std::enable_if_t<is_int_arithmetic_v<T>>>
class foo;


Do note the inline, it will help users of the code to deal with ODR issues.






share|improve this answer






















  • typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
    – Quuxplusone
    Dec 19 '18 at 18:41










  • @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
    – Incomputable
    Dec 19 '18 at 18:43











  • "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
    – Quuxplusone
    Dec 19 '18 at 18:54






  • 2




    Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
    – phresnel
    Dec 20 '18 at 9:14







  • 1




    since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
    – Martin York
    Dec 20 '18 at 11:36



















2














template <typename T, 
sfinae<bool,
std::enable_if_t<std::is_integral_v<T>>,
std::enable_if_t<std::is_arithmetic_v<T>>
> = true /* provide default option, bc reasons */>
class foo;


vs



template <typename T, 
std::enable_if_t<
std::is_integral_v<T>
&& std::is_arithmetic_v<T>,
bool
> = true
class foo;


This version doesn't use your new sfinae template, and uses fewer characters, and seems no less functional.



What more in C++20 we'll have named concepts (and failing that) requires clauses. That will make much SFINAE obsolete.



What more, there are fancier ways to do SFINAE, especially in c++20



There is this:



template<template<class...>class, class...>
struct can_apply;


which tests if a template can be applied to a list of types.



With constexpr lambdas that can appear in template non type parameter argument calculations, we can do:



template<class F>
constexpr auto apply_test(F &&);


which returns an object that, when evaluated on some arguments, returns true_type if you can invoke F on it, and false_type otherwise.



template<class T,
std::enable_if_t<
apply_test((auto a, auto b)RETURNS(a+b))( std::declval<T>(), std::declval<T>() ),
bool
> = true
>
struct foo;


here we test if a type T can be added to itself. (I also use the somewhat ubiquitous RETURNS macro)



Or, more cleanly:



template<class T>
auto foo( T const& lhs, T const& rhs )
requires test_apply(std::plus<T>)(lhs, rhs)


assuming sufficiently SFINAE friendly std::plus<T>.






share|improve this answer




















    Your Answer





    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209992%2fsfinae-proposal-useful-or-unnecessary%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    14















    I just wrote four lines of code...



    template<typename T, typename ...>
    using sfinae = T;

    template<typename T, typename ...>
    using SFINAE = T;



    You should have written just two lines of code — either the first two or the last two. Not both. "There's more than one way to [spell] it" is the Perl way, not the C++ way. ;) (At least not intentionally! Not intentionally pre-C++2a! Now we're getting both std::filesystem and std::fs, that's a less airtight position.)




    As you show, your thing can be used for



    template<class T, 
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true>
    void foo();


    However, this is pretty verbose; surely we'd rather write std::enable_if_t<std::is_integral_v<T> && std::is_arithmetic_v<T>> than std::enable_if_t<std::is_integral_v<T>>, std::enable_if_t<std::is_arithmetic_v<T>>.



    So personally I have a different alias that accomplishes the same goal: bool_if_t.



    template<bool B>
    using bool_if_t = std::enable_if_t<B, bool>;

    template<class T,
    bool_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>
    > = true>
    void foo();


    This has the advantage that the inner expression is written using normal C++ syntax rules. We don't have to remember some arbitrary rule like "x, y means both x and y must be true"; we just write x && y. And if we want to enable this function when either x or y is true, we don't have to invent a new primitive; we just write bool_if_t<x || y>. bool_if_t is much more composable than your sfinae<Ts...>, because bool_if_t can exploit the existing expression syntax of C++.




    Recommended viewing: "A Soupçon of SFINAE" (Arthur O'Dwyer, CppCon 2017). (Yes, that's me.)



    You picked examples that don't really show off the power of sfinae, by the way.
    Consider this example:



    // 3) as a function return type:
    template <typename T>
    sfinae<T,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > const & foo(T const & val);


    You take a "value-space" boolean (is_integral_v<T>), lift it up into "SFINAE-space" with enable_if_t, and then apply a logical AND operation in SFINAE-space using your sfinae alias.



    template <typename T> 
    std::enable_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>, T
    > const & foo2(T const & val);


    Here I take the same "value-space" boolean, perform the logical AND in value space where we have a dedicated operator && for maximum expressiveness; and I lift it up into SFINAE-space after doing the AND. This is clearer and also probably more efficient in terms of compile time.



    Where your version helps is when your values start in SFINAE-space!



    template<class T> 
    sfinae<T,
    decltype(std::declval<T>() + std::declval<T>()),
    T*,
    T&
    > const & foo3(T const & val);


    If we were trying to replicate this code's behavior with the standard tools, we'd say something like



    template<class, class=void> struct has_plus : std::false_type ;
    template<class T> struct has_plus<T, decltype(void(std::declval<T>() + std::declval<T>()))> : std::true_type ;
    template<class T> inline constexpr bool has_plus_v = has_plus<T>::value;

    template<class T>
    std::enable_if_t<
    has_plus_v<T> &&
    not std::is_reference_v<T> &&
    not std::is_void_v<T>, T
    > const & foo4(T const & val);


    That is, we start with a value in SFINAE-space (decltype(std::declval<T>() + std::declval<T>())), lift it into value-space (has_plus_v<T>), do the logical AND in value-space, and lift the result back into SFINAE-space.



    Whereas with your foo3, you start in SFINAE-space, do the logical AND via sfinae<...> without leaving SFINAE-space, and then you're done. Much simpler! But harder to read, I think.




    A simple way to improve readability is to pick a good name. sfinae_space_and<Ts...> might be clearer. Can you think of a way to write sfinae_space_or<Ts...>? How about sfinae_space_not<T>? Does it make sense, maintainability-wise, to provide one without the others?






    share|improve this answer
















    • 5




      Incredible how much one can write about four (two) lines of code. Very informative ;-)
      – t3chb0t
      Dec 19 '18 at 18:42










    • What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
      – Flamefire
      Dec 20 '18 at 11:56










    • @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
      – Quuxplusone
      Dec 20 '18 at 16:28










    • Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
      – Quuxplusone
      Dec 20 '18 at 16:30






    • 1




      @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
      – Quuxplusone
      Dec 21 '18 at 22:23















    14















    I just wrote four lines of code...



    template<typename T, typename ...>
    using sfinae = T;

    template<typename T, typename ...>
    using SFINAE = T;



    You should have written just two lines of code — either the first two or the last two. Not both. "There's more than one way to [spell] it" is the Perl way, not the C++ way. ;) (At least not intentionally! Not intentionally pre-C++2a! Now we're getting both std::filesystem and std::fs, that's a less airtight position.)




    As you show, your thing can be used for



    template<class T, 
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true>
    void foo();


    However, this is pretty verbose; surely we'd rather write std::enable_if_t<std::is_integral_v<T> && std::is_arithmetic_v<T>> than std::enable_if_t<std::is_integral_v<T>>, std::enable_if_t<std::is_arithmetic_v<T>>.



    So personally I have a different alias that accomplishes the same goal: bool_if_t.



    template<bool B>
    using bool_if_t = std::enable_if_t<B, bool>;

    template<class T,
    bool_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>
    > = true>
    void foo();


    This has the advantage that the inner expression is written using normal C++ syntax rules. We don't have to remember some arbitrary rule like "x, y means both x and y must be true"; we just write x && y. And if we want to enable this function when either x or y is true, we don't have to invent a new primitive; we just write bool_if_t<x || y>. bool_if_t is much more composable than your sfinae<Ts...>, because bool_if_t can exploit the existing expression syntax of C++.




    Recommended viewing: "A Soupçon of SFINAE" (Arthur O'Dwyer, CppCon 2017). (Yes, that's me.)



    You picked examples that don't really show off the power of sfinae, by the way.
    Consider this example:



    // 3) as a function return type:
    template <typename T>
    sfinae<T,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > const & foo(T const & val);


    You take a "value-space" boolean (is_integral_v<T>), lift it up into "SFINAE-space" with enable_if_t, and then apply a logical AND operation in SFINAE-space using your sfinae alias.



    template <typename T> 
    std::enable_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>, T
    > const & foo2(T const & val);


    Here I take the same "value-space" boolean, perform the logical AND in value space where we have a dedicated operator && for maximum expressiveness; and I lift it up into SFINAE-space after doing the AND. This is clearer and also probably more efficient in terms of compile time.



    Where your version helps is when your values start in SFINAE-space!



    template<class T> 
    sfinae<T,
    decltype(std::declval<T>() + std::declval<T>()),
    T*,
    T&
    > const & foo3(T const & val);


    If we were trying to replicate this code's behavior with the standard tools, we'd say something like



    template<class, class=void> struct has_plus : std::false_type ;
    template<class T> struct has_plus<T, decltype(void(std::declval<T>() + std::declval<T>()))> : std::true_type ;
    template<class T> inline constexpr bool has_plus_v = has_plus<T>::value;

    template<class T>
    std::enable_if_t<
    has_plus_v<T> &&
    not std::is_reference_v<T> &&
    not std::is_void_v<T>, T
    > const & foo4(T const & val);


    That is, we start with a value in SFINAE-space (decltype(std::declval<T>() + std::declval<T>())), lift it into value-space (has_plus_v<T>), do the logical AND in value-space, and lift the result back into SFINAE-space.



    Whereas with your foo3, you start in SFINAE-space, do the logical AND via sfinae<...> without leaving SFINAE-space, and then you're done. Much simpler! But harder to read, I think.




    A simple way to improve readability is to pick a good name. sfinae_space_and<Ts...> might be clearer. Can you think of a way to write sfinae_space_or<Ts...>? How about sfinae_space_not<T>? Does it make sense, maintainability-wise, to provide one without the others?






    share|improve this answer
















    • 5




      Incredible how much one can write about four (two) lines of code. Very informative ;-)
      – t3chb0t
      Dec 19 '18 at 18:42










    • What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
      – Flamefire
      Dec 20 '18 at 11:56










    • @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
      – Quuxplusone
      Dec 20 '18 at 16:28










    • Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
      – Quuxplusone
      Dec 20 '18 at 16:30






    • 1




      @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
      – Quuxplusone
      Dec 21 '18 at 22:23













    14












    14








    14







    I just wrote four lines of code...



    template<typename T, typename ...>
    using sfinae = T;

    template<typename T, typename ...>
    using SFINAE = T;



    You should have written just two lines of code — either the first two or the last two. Not both. "There's more than one way to [spell] it" is the Perl way, not the C++ way. ;) (At least not intentionally! Not intentionally pre-C++2a! Now we're getting both std::filesystem and std::fs, that's a less airtight position.)




    As you show, your thing can be used for



    template<class T, 
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true>
    void foo();


    However, this is pretty verbose; surely we'd rather write std::enable_if_t<std::is_integral_v<T> && std::is_arithmetic_v<T>> than std::enable_if_t<std::is_integral_v<T>>, std::enable_if_t<std::is_arithmetic_v<T>>.



    So personally I have a different alias that accomplishes the same goal: bool_if_t.



    template<bool B>
    using bool_if_t = std::enable_if_t<B, bool>;

    template<class T,
    bool_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>
    > = true>
    void foo();


    This has the advantage that the inner expression is written using normal C++ syntax rules. We don't have to remember some arbitrary rule like "x, y means both x and y must be true"; we just write x && y. And if we want to enable this function when either x or y is true, we don't have to invent a new primitive; we just write bool_if_t<x || y>. bool_if_t is much more composable than your sfinae<Ts...>, because bool_if_t can exploit the existing expression syntax of C++.




    Recommended viewing: "A Soupçon of SFINAE" (Arthur O'Dwyer, CppCon 2017). (Yes, that's me.)



    You picked examples that don't really show off the power of sfinae, by the way.
    Consider this example:



    // 3) as a function return type:
    template <typename T>
    sfinae<T,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > const & foo(T const & val);


    You take a "value-space" boolean (is_integral_v<T>), lift it up into "SFINAE-space" with enable_if_t, and then apply a logical AND operation in SFINAE-space using your sfinae alias.



    template <typename T> 
    std::enable_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>, T
    > const & foo2(T const & val);


    Here I take the same "value-space" boolean, perform the logical AND in value space where we have a dedicated operator && for maximum expressiveness; and I lift it up into SFINAE-space after doing the AND. This is clearer and also probably more efficient in terms of compile time.



    Where your version helps is when your values start in SFINAE-space!



    template<class T> 
    sfinae<T,
    decltype(std::declval<T>() + std::declval<T>()),
    T*,
    T&
    > const & foo3(T const & val);


    If we were trying to replicate this code's behavior with the standard tools, we'd say something like



    template<class, class=void> struct has_plus : std::false_type ;
    template<class T> struct has_plus<T, decltype(void(std::declval<T>() + std::declval<T>()))> : std::true_type ;
    template<class T> inline constexpr bool has_plus_v = has_plus<T>::value;

    template<class T>
    std::enable_if_t<
    has_plus_v<T> &&
    not std::is_reference_v<T> &&
    not std::is_void_v<T>, T
    > const & foo4(T const & val);


    That is, we start with a value in SFINAE-space (decltype(std::declval<T>() + std::declval<T>())), lift it into value-space (has_plus_v<T>), do the logical AND in value-space, and lift the result back into SFINAE-space.



    Whereas with your foo3, you start in SFINAE-space, do the logical AND via sfinae<...> without leaving SFINAE-space, and then you're done. Much simpler! But harder to read, I think.




    A simple way to improve readability is to pick a good name. sfinae_space_and<Ts...> might be clearer. Can you think of a way to write sfinae_space_or<Ts...>? How about sfinae_space_not<T>? Does it make sense, maintainability-wise, to provide one without the others?






    share|improve this answer













    I just wrote four lines of code...



    template<typename T, typename ...>
    using sfinae = T;

    template<typename T, typename ...>
    using SFINAE = T;



    You should have written just two lines of code — either the first two or the last two. Not both. "There's more than one way to [spell] it" is the Perl way, not the C++ way. ;) (At least not intentionally! Not intentionally pre-C++2a! Now we're getting both std::filesystem and std::fs, that's a less airtight position.)




    As you show, your thing can be used for



    template<class T, 
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true>
    void foo();


    However, this is pretty verbose; surely we'd rather write std::enable_if_t<std::is_integral_v<T> && std::is_arithmetic_v<T>> than std::enable_if_t<std::is_integral_v<T>>, std::enable_if_t<std::is_arithmetic_v<T>>.



    So personally I have a different alias that accomplishes the same goal: bool_if_t.



    template<bool B>
    using bool_if_t = std::enable_if_t<B, bool>;

    template<class T,
    bool_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>
    > = true>
    void foo();


    This has the advantage that the inner expression is written using normal C++ syntax rules. We don't have to remember some arbitrary rule like "x, y means both x and y must be true"; we just write x && y. And if we want to enable this function when either x or y is true, we don't have to invent a new primitive; we just write bool_if_t<x || y>. bool_if_t is much more composable than your sfinae<Ts...>, because bool_if_t can exploit the existing expression syntax of C++.




    Recommended viewing: "A Soupçon of SFINAE" (Arthur O'Dwyer, CppCon 2017). (Yes, that's me.)



    You picked examples that don't really show off the power of sfinae, by the way.
    Consider this example:



    // 3) as a function return type:
    template <typename T>
    sfinae<T,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > const & foo(T const & val);


    You take a "value-space" boolean (is_integral_v<T>), lift it up into "SFINAE-space" with enable_if_t, and then apply a logical AND operation in SFINAE-space using your sfinae alias.



    template <typename T> 
    std::enable_if_t<
    std::is_integral_v<T> &&
    std::is_arithmetic_v<T>, T
    > const & foo2(T const & val);


    Here I take the same "value-space" boolean, perform the logical AND in value space where we have a dedicated operator && for maximum expressiveness; and I lift it up into SFINAE-space after doing the AND. This is clearer and also probably more efficient in terms of compile time.



    Where your version helps is when your values start in SFINAE-space!



    template<class T> 
    sfinae<T,
    decltype(std::declval<T>() + std::declval<T>()),
    T*,
    T&
    > const & foo3(T const & val);


    If we were trying to replicate this code's behavior with the standard tools, we'd say something like



    template<class, class=void> struct has_plus : std::false_type ;
    template<class T> struct has_plus<T, decltype(void(std::declval<T>() + std::declval<T>()))> : std::true_type ;
    template<class T> inline constexpr bool has_plus_v = has_plus<T>::value;

    template<class T>
    std::enable_if_t<
    has_plus_v<T> &&
    not std::is_reference_v<T> &&
    not std::is_void_v<T>, T
    > const & foo4(T const & val);


    That is, we start with a value in SFINAE-space (decltype(std::declval<T>() + std::declval<T>())), lift it into value-space (has_plus_v<T>), do the logical AND in value-space, and lift the result back into SFINAE-space.



    Whereas with your foo3, you start in SFINAE-space, do the logical AND via sfinae<...> without leaving SFINAE-space, and then you're done. Much simpler! But harder to read, I think.




    A simple way to improve readability is to pick a good name. sfinae_space_and<Ts...> might be clearer. Can you think of a way to write sfinae_space_or<Ts...>? How about sfinae_space_not<T>? Does it make sense, maintainability-wise, to provide one without the others?







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Dec 19 '18 at 18:38









    Quuxplusone

    11.3k11959




    11.3k11959







    • 5




      Incredible how much one can write about four (two) lines of code. Very informative ;-)
      – t3chb0t
      Dec 19 '18 at 18:42










    • What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
      – Flamefire
      Dec 20 '18 at 11:56










    • @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
      – Quuxplusone
      Dec 20 '18 at 16:28










    • Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
      – Quuxplusone
      Dec 20 '18 at 16:30






    • 1




      @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
      – Quuxplusone
      Dec 21 '18 at 22:23












    • 5




      Incredible how much one can write about four (two) lines of code. Very informative ;-)
      – t3chb0t
      Dec 19 '18 at 18:42










    • What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
      – Flamefire
      Dec 20 '18 at 11:56










    • @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
      – Quuxplusone
      Dec 20 '18 at 16:28










    • Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
      – Quuxplusone
      Dec 20 '18 at 16:30






    • 1




      @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
      – Quuxplusone
      Dec 21 '18 at 22:23







    5




    5




    Incredible how much one can write about four (two) lines of code. Very informative ;-)
    – t3chb0t
    Dec 19 '18 at 18:42




    Incredible how much one can write about four (two) lines of code. Very informative ;-)
    – t3chb0t
    Dec 19 '18 at 18:42












    What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
    – Flamefire
    Dec 20 '18 at 11:56




    What's the use of bool_if_t = std::enable_if_t<B, bool>;? IMO it hides the intent behind it by removing the informative "enable_if" part.
    – Flamefire
    Dec 20 '18 at 11:56












    @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
    – Quuxplusone
    Dec 20 '18 at 16:28




    @Flamefire: I see your point, but OTOH, the "intent" is usually obvious from context. Significantly, bool_if_t<B>=true is 8 characters shorter than enable_if_t<B, bool>=true. Perhaps most significantly, it eliminates a bikeshed point: if all you have is the low-level enable_if_t, then you'll see people writing enable_if_t<B, XXX>= for many different values of XXX (bool, int, etc). The slightly-higher-level abstraction eliminates that needless variation. (Murphy's Law: if you don't want people fiddling with the knobs, you have to remove the knobs.)
    – Quuxplusone
    Dec 20 '18 at 16:28












    Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
    – Quuxplusone
    Dec 20 '18 at 16:30




    Full disclosure: enable_if_t<B, int>=0 is 4 characters shorter than enable_if_t<B, bool>=true, and int_if_t<B>=0 would be 4 characters shorter than bool_if_t<B>=true. Why did I pick bool?... I don't know. :P Maybe I should switch to int_if_t as my primitive.
    – Quuxplusone
    Dec 20 '18 at 16:30




    1




    1




    @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
    – Quuxplusone
    Dec 21 '18 at 22:23




    @cukier9a7b5: "I was actually trying to implement sfinae_space_not and sfinae_space_or ... Unfortunately [I failed] ... I definitely think that, maintainability-wise, it is a must to provide at least syntax for not, and, or cases." Your thoughts parallel mine! :) I'm about 95% sure that it is impossible to write either sfinae_not or sfinae_or. So, by the laws of syllogism, as you stated: you shouldn't provide sfinae_and, either.
    – Quuxplusone
    Dec 21 '18 at 22:23













    9














    A bit dated...



    First of all, let me say that since C++20 much of SFINAE will be unnecessary. Concepts have SFINAE enabled by default, have much nicer syntax, and have greater reuse capabilities.



    Code Review



    Personally I don't see much improvement over original one. What I would prefer instead of



    template <typename T, typename ... >
    ...


    is



    template <typename T, bool ... Conditionals>
    ... //perform folding


    Even then, people sometimes have non-trivial logic like AND-ing and OR-ing the results of conditionals.



    Much of the complexity lies in that conditional statements, which could be moved away by using something like template variable:



    template <typename T>
    constexpr inline bool supports_xxx = /*is_same, trivial, etc*/;


    and then people could just use



    template <typename T, typename = std::enable_if_t<supports_xxx<T>>>
    ...



    Example



    // 1) within class declaration:
    template <typename T,
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true /* provide default option, bc reasons */>
    class foo;


    is better written as



    template <typename T>
    constexpr inline bool is_int_arithmetic_v = std::is_integral_v<T>
    && std::is_arithmetic_v<T>;

    template <typename T,
    typename = std::enable_if_t<is_int_arithmetic_v<T>>>
    class foo;


    Do note the inline, it will help users of the code to deal with ODR issues.






    share|improve this answer






















    • typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
      – Quuxplusone
      Dec 19 '18 at 18:41










    • @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
      – Incomputable
      Dec 19 '18 at 18:43











    • "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
      – Quuxplusone
      Dec 19 '18 at 18:54






    • 2




      Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
      – phresnel
      Dec 20 '18 at 9:14







    • 1




      since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
      – Martin York
      Dec 20 '18 at 11:36
















    9














    A bit dated...



    First of all, let me say that since C++20 much of SFINAE will be unnecessary. Concepts have SFINAE enabled by default, have much nicer syntax, and have greater reuse capabilities.



    Code Review



    Personally I don't see much improvement over original one. What I would prefer instead of



    template <typename T, typename ... >
    ...


    is



    template <typename T, bool ... Conditionals>
    ... //perform folding


    Even then, people sometimes have non-trivial logic like AND-ing and OR-ing the results of conditionals.



    Much of the complexity lies in that conditional statements, which could be moved away by using something like template variable:



    template <typename T>
    constexpr inline bool supports_xxx = /*is_same, trivial, etc*/;


    and then people could just use



    template <typename T, typename = std::enable_if_t<supports_xxx<T>>>
    ...



    Example



    // 1) within class declaration:
    template <typename T,
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true /* provide default option, bc reasons */>
    class foo;


    is better written as



    template <typename T>
    constexpr inline bool is_int_arithmetic_v = std::is_integral_v<T>
    && std::is_arithmetic_v<T>;

    template <typename T,
    typename = std::enable_if_t<is_int_arithmetic_v<T>>>
    class foo;


    Do note the inline, it will help users of the code to deal with ODR issues.






    share|improve this answer






















    • typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
      – Quuxplusone
      Dec 19 '18 at 18:41










    • @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
      – Incomputable
      Dec 19 '18 at 18:43











    • "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
      – Quuxplusone
      Dec 19 '18 at 18:54






    • 2




      Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
      – phresnel
      Dec 20 '18 at 9:14







    • 1




      since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
      – Martin York
      Dec 20 '18 at 11:36














    9












    9








    9






    A bit dated...



    First of all, let me say that since C++20 much of SFINAE will be unnecessary. Concepts have SFINAE enabled by default, have much nicer syntax, and have greater reuse capabilities.



    Code Review



    Personally I don't see much improvement over original one. What I would prefer instead of



    template <typename T, typename ... >
    ...


    is



    template <typename T, bool ... Conditionals>
    ... //perform folding


    Even then, people sometimes have non-trivial logic like AND-ing and OR-ing the results of conditionals.



    Much of the complexity lies in that conditional statements, which could be moved away by using something like template variable:



    template <typename T>
    constexpr inline bool supports_xxx = /*is_same, trivial, etc*/;


    and then people could just use



    template <typename T, typename = std::enable_if_t<supports_xxx<T>>>
    ...



    Example



    // 1) within class declaration:
    template <typename T,
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true /* provide default option, bc reasons */>
    class foo;


    is better written as



    template <typename T>
    constexpr inline bool is_int_arithmetic_v = std::is_integral_v<T>
    && std::is_arithmetic_v<T>;

    template <typename T,
    typename = std::enable_if_t<is_int_arithmetic_v<T>>>
    class foo;


    Do note the inline, it will help users of the code to deal with ODR issues.






    share|improve this answer














    A bit dated...



    First of all, let me say that since C++20 much of SFINAE will be unnecessary. Concepts have SFINAE enabled by default, have much nicer syntax, and have greater reuse capabilities.



    Code Review



    Personally I don't see much improvement over original one. What I would prefer instead of



    template <typename T, typename ... >
    ...


    is



    template <typename T, bool ... Conditionals>
    ... //perform folding


    Even then, people sometimes have non-trivial logic like AND-ing and OR-ing the results of conditionals.



    Much of the complexity lies in that conditional statements, which could be moved away by using something like template variable:



    template <typename T>
    constexpr inline bool supports_xxx = /*is_same, trivial, etc*/;


    and then people could just use



    template <typename T, typename = std::enable_if_t<supports_xxx<T>>>
    ...



    Example



    // 1) within class declaration:
    template <typename T,
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true /* provide default option, bc reasons */>
    class foo;


    is better written as



    template <typename T>
    constexpr inline bool is_int_arithmetic_v = std::is_integral_v<T>
    && std::is_arithmetic_v<T>;

    template <typename T,
    typename = std::enable_if_t<is_int_arithmetic_v<T>>>
    class foo;


    Do note the inline, it will help users of the code to deal with ODR issues.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 19 '18 at 18:49

























    answered Dec 19 '18 at 18:20









    Incomputable

    6,59021653




    6,59021653











    • typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
      – Quuxplusone
      Dec 19 '18 at 18:41










    • @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
      – Incomputable
      Dec 19 '18 at 18:43











    • "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
      – Quuxplusone
      Dec 19 '18 at 18:54






    • 2




      Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
      – phresnel
      Dec 20 '18 at 9:14







    • 1




      since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
      – Martin York
      Dec 20 '18 at 11:36

















    • typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
      – Quuxplusone
      Dec 19 '18 at 18:41










    • @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
      – Incomputable
      Dec 19 '18 at 18:43











    • "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
      – Quuxplusone
      Dec 19 '18 at 18:54






    • 2




      Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
      – phresnel
      Dec 20 '18 at 9:14







    • 1




      since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
      – Martin York
      Dec 20 '18 at 11:36
















    typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
    – Quuxplusone
    Dec 19 '18 at 18:41




    typename = void_t<things...> doesn't work if you want multiple mutually exclusive templates. bool_if_t<things...> = true works in that case. See youtube.com/watch?v=ybaE9qlhHvw&t=37m00s .
    – Quuxplusone
    Dec 19 '18 at 18:41












    @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
    – Incomputable
    Dec 19 '18 at 18:43





    @Quuxplusone, shouldn't specialization paired with some sort of tag dispatch be used in that case? I believe it also scales better in cases where more than 2 mutually exclusive cases are present. But yeah, my advice is not a good one to make.
    – Incomputable
    Dec 19 '18 at 18:43













    "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
    – Quuxplusone
    Dec 19 '18 at 18:54




    "Shouldn't" is a strong word. ;) I agree that tag dispatch is usually more readable and should be preferred (or if constexpr in C++17). But sometimes tag dispatch can't be used; see the video's example of a conditionally explicit constructor.
    – Quuxplusone
    Dec 19 '18 at 18:54




    2




    2




    Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
    – phresnel
    Dec 20 '18 at 9:14





    Review review: It is "Dated" "since C++20" because it "will be unecessary". Sounds like a plot from Back to the Future IV ;) Anyways, learning SFINAE may still be worthwhile while writing library code that should be backwards compatible and/or useful within older codebases. And compilers still not to catch up a lot 'til they support fully C++20.
    – phresnel
    Dec 20 '18 at 9:14





    1




    1




    since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
    – Martin York
    Dec 20 '18 at 11:36





    since C++20 Still a year off! Since Concepts were initially proposed for C++11 C++14 and C++17 but dropped before the standard was published I am not going to hold my breath until I see them in the ratified standard.
    – Martin York
    Dec 20 '18 at 11:36












    2














    template <typename T, 
    sfinae<bool,
    std::enable_if_t<std::is_integral_v<T>>,
    std::enable_if_t<std::is_arithmetic_v<T>>
    > = true /* provide default option, bc reasons */>
    class foo;


    vs



    template <typename T, 
    std::enable_if_t<
    std::is_integral_v<T>
    && std::is_arithmetic_v<T>,
    bool
    > = true
    class foo;


    This version doesn't use your new sfinae template, and uses fewer characters, and seems no less functional.



    What more in C++20 we'll have named concepts (and failing that) requires clauses. That will make much SFINAE obsolete.



    What more, there are fancier ways to do SFINAE, especially in c++20



    There is this:



    template<template<class...>class, class...>
    struct can_apply;


    which tests if a template can be applied to a list of types.



    With constexpr lambdas that can appear in template non type parameter argument calculations, we can do:



    template<class F>
    constexpr auto apply_test(F &&);


    which returns an object that, when evaluated on some arguments, returns true_type if you can invoke F on it, and false_type otherwise.



    template<class T,
    std::enable_if_t<
    apply_test((auto a, auto b)RETURNS(a+b))( std::declval<T>(), std::declval<T>() ),
    bool
    > = true
    >
    struct foo;


    here we test if a type T can be added to itself. (I also use the somewhat ubiquitous RETURNS macro)



    Or, more cleanly:



    template<class T>
    auto foo( T const& lhs, T const& rhs )
    requires test_apply(std::plus<T>)(lhs, rhs)


    assuming sufficiently SFINAE friendly std::plus<T>.






    share|improve this answer

























      2














      template <typename T, 
      sfinae<bool,
      std::enable_if_t<std::is_integral_v<T>>,
      std::enable_if_t<std::is_arithmetic_v<T>>
      > = true /* provide default option, bc reasons */>
      class foo;


      vs



      template <typename T, 
      std::enable_if_t<
      std::is_integral_v<T>
      && std::is_arithmetic_v<T>,
      bool
      > = true
      class foo;


      This version doesn't use your new sfinae template, and uses fewer characters, and seems no less functional.



      What more in C++20 we'll have named concepts (and failing that) requires clauses. That will make much SFINAE obsolete.



      What more, there are fancier ways to do SFINAE, especially in c++20



      There is this:



      template<template<class...>class, class...>
      struct can_apply;


      which tests if a template can be applied to a list of types.



      With constexpr lambdas that can appear in template non type parameter argument calculations, we can do:



      template<class F>
      constexpr auto apply_test(F &&);


      which returns an object that, when evaluated on some arguments, returns true_type if you can invoke F on it, and false_type otherwise.



      template<class T,
      std::enable_if_t<
      apply_test((auto a, auto b)RETURNS(a+b))( std::declval<T>(), std::declval<T>() ),
      bool
      > = true
      >
      struct foo;


      here we test if a type T can be added to itself. (I also use the somewhat ubiquitous RETURNS macro)



      Or, more cleanly:



      template<class T>
      auto foo( T const& lhs, T const& rhs )
      requires test_apply(std::plus<T>)(lhs, rhs)


      assuming sufficiently SFINAE friendly std::plus<T>.






      share|improve this answer























        2












        2








        2






        template <typename T, 
        sfinae<bool,
        std::enable_if_t<std::is_integral_v<T>>,
        std::enable_if_t<std::is_arithmetic_v<T>>
        > = true /* provide default option, bc reasons */>
        class foo;


        vs



        template <typename T, 
        std::enable_if_t<
        std::is_integral_v<T>
        && std::is_arithmetic_v<T>,
        bool
        > = true
        class foo;


        This version doesn't use your new sfinae template, and uses fewer characters, and seems no less functional.



        What more in C++20 we'll have named concepts (and failing that) requires clauses. That will make much SFINAE obsolete.



        What more, there are fancier ways to do SFINAE, especially in c++20



        There is this:



        template<template<class...>class, class...>
        struct can_apply;


        which tests if a template can be applied to a list of types.



        With constexpr lambdas that can appear in template non type parameter argument calculations, we can do:



        template<class F>
        constexpr auto apply_test(F &&);


        which returns an object that, when evaluated on some arguments, returns true_type if you can invoke F on it, and false_type otherwise.



        template<class T,
        std::enable_if_t<
        apply_test((auto a, auto b)RETURNS(a+b))( std::declval<T>(), std::declval<T>() ),
        bool
        > = true
        >
        struct foo;


        here we test if a type T can be added to itself. (I also use the somewhat ubiquitous RETURNS macro)



        Or, more cleanly:



        template<class T>
        auto foo( T const& lhs, T const& rhs )
        requires test_apply(std::plus<T>)(lhs, rhs)


        assuming sufficiently SFINAE friendly std::plus<T>.






        share|improve this answer












        template <typename T, 
        sfinae<bool,
        std::enable_if_t<std::is_integral_v<T>>,
        std::enable_if_t<std::is_arithmetic_v<T>>
        > = true /* provide default option, bc reasons */>
        class foo;


        vs



        template <typename T, 
        std::enable_if_t<
        std::is_integral_v<T>
        && std::is_arithmetic_v<T>,
        bool
        > = true
        class foo;


        This version doesn't use your new sfinae template, and uses fewer characters, and seems no less functional.



        What more in C++20 we'll have named concepts (and failing that) requires clauses. That will make much SFINAE obsolete.



        What more, there are fancier ways to do SFINAE, especially in c++20



        There is this:



        template<template<class...>class, class...>
        struct can_apply;


        which tests if a template can be applied to a list of types.



        With constexpr lambdas that can appear in template non type parameter argument calculations, we can do:



        template<class F>
        constexpr auto apply_test(F &&);


        which returns an object that, when evaluated on some arguments, returns true_type if you can invoke F on it, and false_type otherwise.



        template<class T,
        std::enable_if_t<
        apply_test((auto a, auto b)RETURNS(a+b))( std::declval<T>(), std::declval<T>() ),
        bool
        > = true
        >
        struct foo;


        here we test if a type T can be added to itself. (I also use the somewhat ubiquitous RETURNS macro)



        Or, more cleanly:



        template<class T>
        auto foo( T const& lhs, T const& rhs )
        requires test_apply(std::plus<T>)(lhs, rhs)


        assuming sufficiently SFINAE friendly std::plus<T>.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Dec 19 '18 at 19:47









        Yakk

        53339




        53339



























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209992%2fsfinae-proposal-useful-or-unnecessary%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown






            Popular posts from this blog

            How to check contact read email or not when send email to Individual?

            Bahrain

            Postfix configuration issue with fips on centos 7; mailgun relay