SFINAE(enable_if) on template arguments without variable specifier - c++

This is the code:
#include <iostream>
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
int main() {
short int i = 1; // code does not compile if type of i is not integral
std::cout << std::boolalpha;
std::cout << "i is odd: " << is_odd(i) << std::endl;
std::cout << "i is even: " << is_even(i) << std::endl;
return 0;
}
I am trying to learn proper usage of enable_if, i understand if it's used as a return type specifier is that: the compiler will ignore the code. Meaning, the function won't be in the binary file.
I am somehow confused if it's used in a template argument. Based from the code above, it says there that the second template argument is only valid if T is an integral type But i am confused what is the purpose of that second argument?
I removed it and changed it to:
template < class T>
bool is_even (T i) {return !bool(i%2);}
and it still works fine. Can someone clarify me what's the real purpose of it? There's no variable specifier on it as well.
Or maybe it only serves as a checker if ill do something like
template < class T,
class B= typename std::enable_if<std::is_integral<T>::value>::type>
Allowing me to access B on my code(can be true or false) ?

You are using it with an integral type. It will only fail when used with a non-integral type. The following should fail to compile:
float f;
is_even(f);
Note that with a C++14 compiler you can write:
template <class T,
class = std::enable_if_t<std::is_integral<T>::value>>
However, you may want to use a static_assert here, since the function probably does not make sense for non integral types, and it will give a better error message at compile time.
Another usage of additional parameters in SFINAE is to lower the preference for a given function. The template with more template arguments with be less likely to get selected than the one with less templates arguments.

The purpose of the enable_if in this case is to cause a compilation error when the deduced template argument for T is not an integral type. We can see from cppreference/is_integral that integral types are integers, characters and their signed and unsigned variants.
For any other type you will get an error that looks like:
main.cpp:21:32: error: no matching function for call to 'is_odd'
std::cout << "i is odd: " << is_odd(NotIntegral()) << std::endl;
^~~~~~
main.cpp:6:25: note: candidate template ignored: disabled by 'enable_if' [with T = NotIntegral]
typename std::enable_if<std::is_integral<T>::value,bool>::type
I understand if it's used as a return type specifier is that the compiler will ignore the code
This isn't true. The return type is evaluated just like any other part of the declaration. See Why should I avoid std::enable_if in function signatures. The choice of placement of the std::enable_if has its pros and cons.
But i am confused what is the purpose of that second argument?
Consider the example where you have a function named foo that takes some T.
template<class T> void foo(T);
You want to restrict one overload of foo whose T's have a value member equal to 1, and want to use another overload for when T::value does not equal 1. If we simply have this:
template<class T> void foo(T); // overload for T::value == 1
template<class T> void foo(T); // overload for T::value != 1
How does this convey to the compiler that you want to use two separate overloads for two separate things? It doesn't. They are both ambiguous function calls:
template<std::size_t N>
struct Widget : std::integral_constant<std::size_t, N> { };
int main() {
Widget<1> w1;
Widget<2> w2;
foo(w1); // Error! Ambiguous!
foo(w2); // Error! Ambiguous!
}
You will need to use SFINAE to reject the templates based on our condition:
template<class T, class = std::enable_if_t<T::value == 1>* = nullptr>
void foo(T); // #1
template<class T, class = std::enable_if_t<T::value != 1>* = nullptr>
void foo(T); // #2
Now the correct ones are called:
foo(w1); // OK! Chooses #1
foo(w2); // OK! Chooses #2
What makes this work is the way std::enable_if is built. If the condition in its first parameter is true, it provides a member typedef named type equal to the second parameter (defaulted to void). Otherwise, if the condition is false, it does not provided one. A reasonable implementation could be:
template<bool, class R = void>
struct enable_if { using type = R; };
template<class R>
struct enable_if<false, R> { /* empty */ };
So if the condition fails, we will be trying to access a ::type member that doesn't exist (remember that std::enable_if_t is an alias for std::enable_if<...>::type). The code would be ill-formed, but instead of it causing a hard error, during overload resolution the template is simply rejected from the candidate set and other overloads (if present) are used instead.
This is a simple explanation of SFINAE. See the cppreference page for more information.
In your case there is only one overload of is_odd and is_even, so if you pass NotIntegral to them, they will fail because their respective overloads are taken out of the candidate set, and since there are no more overloads to evaluate overload resolution fails with the aforementioned error. This could be cleaned up a bit with a static_assert message instead.
template<class T>
bool is_even (T i) {
static_assert(std::is_integral<T>::value, "T must be an integral type");
return !bool(i%2);
}
Now you will no longer be given a weird looking error message but your own custom one. This also rules out SFINAE since the static_assert is evaluated after template arguments are substituted, so it's your choice on which route to take. I would recommend the assertion since it's much cleaner and there is no need for SFINAE here.

Related

How are template specializations chosen with default arguments and more

https://stackoverflow.com/a/22487113/4416169
in the following answer we can see the code in scrutiny:
#include <iostream>
#include <type_traits>
template <typename T, T N, typename = void >
struct X {
static const bool isZero = false;
};
template <typename T, T N>
struct X < T, N, typename std::enable_if<N == 0>::type > {
static const bool isZero = true;
};
int main(int argc, char* argv[]) {
std::cout << X <int, 0>::isZero << std::endl;
std::cout << X <int, 1>::isZero << std::endl;
return 0;
}
https://stackoverflow.com/a/35652391/4416169 here we can see how templates are chosen:
Choosing a template specialization happens in five steps:
Take the primary template declaration.
Fill in user-specified template arguments.
Function templates
only: Deduce additional template arguments.
Use defaults for
remaining template arguments.
Use the partial ordering
algorithm (C++14 14.5.6.2) to choose the best-matching
specialization.
firstly, im aware that SFINAE will take care of excluding the struct with the std::enable_if<N==0> as it will not have the ::type defined (making it compile to error, but of course substitution failure is NOT an ERROR, just exclude the template from the final specialization options, so beautiful) when N != 0. Since N!=0 always results in the formerly defined template being chosen(the unspecialized one), I wonder why is it that N=0 chooses the latterly defined template(the specialized one) over the former.
Thus following these steps we can further scrutinize(in the case of T=int and N=0):
template<typename T, T N, typename = void>
T is int, N is 0; template<int,0,(unnamed-type)>
do nothing we are not a function.
(unnamed-type) is void; template<int,0,void>
this is explained here: https://stackoverflow.com/a/17008568/4416169
We know now that when we try to access X<int,0>::isZero there are two options, the one with the default template type set to void by default, and the one where that same template type is set to void but not by default but explicitly.
Does the compiler always prefer the template(out of all available template specializations) with the least arguments?
Moreover and expanding: would this mean that the template with the least actual arguments is chosen over the one with the same amount of explicit(explicit as in non-defaulted or obligatory) arguments but that has the remainder set to a default value?
To be even more clear:
template<typename T, T N, typename C = void>
struct defTempArgStruct {
static constexpr unsigned int value = 0;
};
template<typename T, T N>
struct defTempArgStruct<T,N>
{
static constexpr unsigned int value = 99;
};
int main()
{
std::cout << defTempArgStruct<int, 2>::value << std::endl;
}
why does that code choose the second specialized template(<typename T, T N>),displaying 99, over the first? And why does it choose the first, displaying 0, when the second, specialized, one changes to be:
template<typename T, T N>
struct defTempArgStruct<T,N,float>
{
static constexpr unsigned int value = 99;
};
very confusing.
PLEASE be eager to correct anything anywhere that im getting wrong!
A viable partial specialization is always preferred over the primary template:
[temp.class.spec.match]/1 When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
(1.1) — If exactly one matching specialization is found, the instantiation is generated from that specialization.
(1.2) — If more than one matching specialization is found, the partial order rules (17.5.5.2) are used to determine whether one of the specializations is more specialized than the others. If none of the specializations is more specialized than all of the other matching specializations, then the use of the class template is ambiguous and the program is ill-formed.
(1.3) — If no matches are found, the instantiation is generated from the primary template.

Template specialization for SFINAE

I have been working with templates for quite some time now but recently I encountered a weird template programming for SFINAE.
My question is why do we write
typename = {something} as a template parameter in C++?
That is simply how SFINAE works ;)
As you know, you have to "create a failure" within the template declaration and not inside the template definition. As this:
template < typename X, typename = ... here is the code which may generate an error during instantiation >
void Bla() {}
The only chance to put some "code" in the declaration is to define something in the template parameter list or inside the template function declaration itself like:
template < typename X>
void Bla( ... something which may generates an error ) {}
Example:
template <typename T, typename = std::enable_if_t< std::is_same_v< int, T>>>
void Bla()
{
std::cout << "with int" << std::endl;
}
template <typename T, typename = std::enable_if_t< !std::is_same_v< int, T>>>
void Bla(int=0)
{
std::cout << "with something else" << std::endl;
}
int main()
{
Bla<int>();
Bla<std::string>();
}
But what is the background of "creating an substitution failure" here?
The trick is somewhere behind std::enable_if. We can also use it without that:
template <typename T, typename = char[ std::is_same_v< int, T>]>
void Bla()
{
std::cout << "with int" << std::endl;
}
template <typename T, typename = char[ !std::is_same_v< int, T>]>
void Bla(int=0)
{
std::cout << "with something else" << std::endl;
}
Take a look on: typename = char[ !std::is_same_v< int, T>]
Here std::is_same_v gives us a bool value back which is casted to 0 if not valid and to any postive number if valid. And creating a char[0] is simply an error in c++! So with the negation of our expression with ! we got one for "is int" and one for "is not int". Once it tries to create an array of size 0, which is a failure and the template will not be instantiated and once it generates the type char[!0] which is a valid expression.
And as the last step:
... typename = ...
is meant as: there will be defined a type, here without a template parameter name, as the parameter itself is not used later. You also can write:
... typename X = ...
but as X is not used, leave it!
Summary: You have to provide some code, which generates an error or not, depending on the type or value of a given expression. The expression must be part of the function declaration or part of the template parameter list. It is not allowed to put an error inside the function/class definition, as this will not longer be "not an error" in the sense of SFINAE.
Update: Can the results of SFINAE expressions be used for further expressions: Yes
Example:
template < typename TYPE >
void CheckForType()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template <typename T, typename X = std::enable_if_t< std::is_same_v< int, T>, float>>
void Bla()
{
std::cout << "with int" << std::endl;
CheckForType<X>();
}
template <typename T, typename X = std::enable_if_t< !std::is_same_v< int, T>, double >>
void Bla(int=0)
{
std::cout << "with something else" << std::endl;
CheckForType<X>();
}
int main()
{
Bla<int>();
Bla<std::string>();
}
Output:
with int
void CheckForType() [with TYPE = float]
with something else
void CheckForType() [with TYPE = double]
Explanation:
std::enable_if has a second template parameter which is used as return type, if the first parameter of it will be true. As this, you can use that type here. As you can see, the function CheckForType is called with this defined type for X from the SFINAE expression.
An example :
//for enum types.
template <typename T, typename std::enable_if<std::is_enum_v<T>, int>::type = 0>
void Foo(T value)
{
//stuff..
}
I only want this Foo to be picked if T is an enum type. We can achieve this using the rule of Substitute Failure Is Not An Error (SFINAE).
This rule states that if there's a failure during substitution this shouldn't be a compilation error. The compiler should only eliminate the function for this substitution.
So then, how do we make sure Foo is only called with an enum type? Well that's easy, we find a way to trigger a substitution failure!
If we check cppreference for std::enable_if it says:
template< bool B, class T = void >
struct enable_if;
If B is true, std::enable_if has a public member typedef type, equal
to T; otherwise, there is no member typedef.
This means, if std::is_enum<T>::value is true (which is the case for enum types for T) then B will be true and thus type will be a valid type, an int as specified.
If however std::is_enum::value is false then B will be false and thus type won't even exist. In this case the code is trying to use ::type while it doesn't exist and thus that's a substitution error. So SFINAE kicks in and eliminates it from the candidate list.
The reason we use = 0 is to provide a default template parameter value since we're not actually interested in using this type or its value, we only use it to trigger SFINAE.
This construct is a nameless template parameter supplied with a default argument. Normally a template parameter would look like
typename <identifier> [ = <type> ]
but the identifier may be omitted.
You can see this construction often in SFINAE templates because it's a convenient way to enable a specialisation if and only if some condition holds, or if some piece of code is valid. Thus one can often see
template <typename T, typename = std::enable_if<(some-constexpr-involving-T)>::type> ...
If the expression happens to evaluate to true, all is well: std::enable_if<true>::type is just void, and we have a good working template specialisation.
Now if the expression above happens to be false, std::enable_if<false> doesn't have a member named type, Substitution Failure (the SF in SFINAE) happens, and the template specialisation is not considered.
And here the role of tge parameter ends. It is not used for anything in the template itself, so it doesn't need a name.

Overload resolution and explicit template arguments

The following code prints "func 2".
Why does the compiler treat the second template as a better match, in presence of explicit (not deduced) template arguments? Why is there no ambiguity?
I'd appreciate quotations from the C++ standard.
#include <iostream>
template<class T>
struct identity
{
typedef T type;
};
template<class T>
void func(T)
{
std::cout << "func 1\n";
}
template<class T>
void func(typename identity<T>::type)
{
std::cout << "func 2\n";
}
int main()
{
func<int>(1);
}
Both candidates are viable and take identical arguments, so the overload resolution process falls back to the last tiebreaker: partial ordering of function templates [temp.func.order].
The rule is that we synthesize a new type for each template type parameter and attempt to perform deduction on each other overload. For 1, we synthesize a type Unique1, which fails deduction on 2 because T is a non-deduced context. For 2, we synthesize a type Unique2, which succeeds deducing T = typename identity<Unique2>::type. Since deduction succeeds in one direction and not the other, that makes 2 more specialized than 1, hence it's preferred.
Note that template partial ordering rules are somewhat incomplete in the standard. If you simply add another argument of type T, the preference flips.

implicit instantiation of undefined template 'class'

When trying to offer functions for const and non-const template arguments in my library I came across a strange problem. The following source code is a minimal example phenomenon:
#include <iostream>
template<typename some_type>
struct some_meta_class;
template<>
struct some_meta_class<int>
{
typedef void type;
};
template<typename some_type>
struct return_type
{
typedef typename some_meta_class< some_type >::type test;
typedef void type;
};
template<typename type>
typename return_type<type>::type foo( type & in )
{
std::cout << "non-const" << std::endl;
}
template<typename type>
void foo( type const & in )
{
std::cout << "const" << std::endl;
}
int main()
{
int i;
int const & ciref = i;
foo(ciref);
}
I tried to implement a non-const version and a const version for foo but unfortunately this code won't compile on CLANG 3.0 and gcc 4.6.3.
main.cpp:18:22: error: implicit instantiation of undefined template
'some_meta_class'
So for some reason the compiler wants to use the non-const version of foo for a const int-reference. This obviously leads to the error above because there is no implementation for some_meta_class. The strange thing is, that if you do one of the following changes, the code compile well and works:
uncomment/remove the non-const version
uncomemnt/remove the typedef of return_type::test
This example is of course minimalistic and pure academic. In my library I came across this problem because the const and non-const version return different types. I managed this problem by using a helper class which is partially specialized.
But why does the example above result in such strange behaviour? Why doesn't the compiler want to use the non-const version where the const version is valid and and matches better?
The reason is the way function call resolution is performed, together with template argument deduction and substitution.
Firstly, name lookup is performed. This gives you two functions with a matching name foo().
Secondly, type deduction is performed: for each of the template functions with a matching name, the compiler tries to deduce the function template arguments which would yield a viable match. The error you get happens in this phase.
Thirdly, overload resolution enters the game. This is only after type deduction has been performed and the signatures of the viable functions for resolving the call have been determined, which makes sense: the compiler can meaningfully resolve your function call only after it has found out the exact signature of all the candidates.
The fact that you get an error related to the non-const overload is not because the compiler chooses it as a most viable candidate for resolving the call (that would be step 3), but because the compiler produces an error while instantiating its return type to determine its signature, during step 2.
It is not entirely obvious why this results in an error though, because one might expect that SFINAE applies (Substitution Failure Is Not An Error). To clarify this, we might consider a simpler example:
template<typename T> struct X { };
template<typename T> typename X<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // Selects overload 2
}
In this example, SFINAE applies: during step 2, the compiler will deduce T for each of the two overloads above, and try to determine their signatures. In case of overload 1, this results in a substitution failure: X<const int> does not define any type (no typedef in X). However, due to SFINAE, the compiler simply discards it and finds that overload 2 is a viable match. Thus, it picks it.
Let's now change the example slightly in a way that mirrors your example:
template<typename T> struct X { };
template<typename Y>
struct R { typedef typename X<Y>::type type; };
// Notice the small change from X<T> into R<T>!
template<typename T> typename R<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // ERROR! Cannot instantiate R<int const>
}
What has changed is that overload 1 no longer returns X<T>::type, but rather R<T>::type. This is in turn the same as X<T>::type because of the typedef declaration in R, so one might expect it to yield the same result. However, in this case you get a compilation error. Why?
The Standard has the answer (Paragraph 14.8.3/8):
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
Clearly, the second example (as well as yours) generates an error in a nested context, so SFINAE does not apply. I believe this answers your question.
By the way, it is interesting to notice, that this has changed since C++03, which more generally stated (Paragraph 14.8.2/2):
[...] If a substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails. [...]
If you are curious about the reasons why things have changed, this paper might give you an idea.

function resolution failed when return type is deduced from enclosed template class

I have been trying to implement a complex number class for fixed point types where the result type of the multiply operation will be a function of the input types. I need to have functions where I can do multiply complex by complex and also complex by real number.
This essentially is a simplified version of the code. Where A is my complex type.
template<typename T1, typename T2> struct rt {};
template<> struct rt<double, double> {
typedef double type;
};
//forward declaration
template<typename T> struct A;
template<typename T1, typename T2>
struct a_rt {
typedef A<typename rt<T1,T2>::type> type;
};
template <typename T>
struct A {
template<typename T2>
typename a_rt<T,T2>::type operator*(const T2& val) const {
typename a_rt<T,T2>::type ret;
cout << "T2& called" << endl;
return ret;
}
template<typename T2>
typename a_rt<T,T2>::type operator*(const A<T2>& val) const {
typename a_rt<T,T2>::type ret;
cout << "A<T2>& called" << endl;
return ret;
}
};
TEST(TmplClassFnOverload, Test) {
A<double> a;
A<double> b;
double c;
a * b;
a * c;
}
The code fails to compile because the compiler is trying to instantiate the a_rt template with double and A<double>. I don't know what is going on under the hood since I imagine the compiler should pick the more specialized operator*(A<double>&) so a_rt will only be instantiated with <double, double> as arguments.
Would you please explain to me why this would not work?
And if this is a limitation, how should I work around this.
Thanks a tonne!
unittest.cpp: In instantiation of 'a_rt<double, A<double> >':
unittest.cpp:198: instantiated from here
unittest.cpp:174: error: no type named 'type' in 'struct rt<double, A<double> >'
Update
The compiler appears to be happy with the following change. There is some subtlety I'm missing here. Appreciate someone who can walk me through what the compiler is doing in both cases.
template<typename T2>
A<typename rt<T,T2>::type> operator*(const T2& val) const {
A<typename rt<T,T2>::type> ret;
cout << "T2& called" << endl;
return ret;
}
template<typename T2>
A<typename rt<T,T2>::type> operator*(const A<T2>& val) const {
A<typename rt<T,T2>::type> ret;
cout << "A<T2>& called" << endl;
return ret;
}
Resolving function calls in C++ proceeds in five phases:
name lookup: this finds two versions of operator*
template argument deduction: this will be applied to all functions found in step 1)
overload resolution: the best match will be selected
access control: can the best match in fact be invoked (i.e. is it not a private member)
virtuality: if virtual function are involved, a lookup in the vtable might be required
First note that the return type is never ever being deduced. You simply cannot overload on return type. The template arguments to operator* are being deduced and then substituted into the return type template.
So what happens at the call a * b;? First, both versions of operator* have their arguments deduced. For the first overload, T2 is deduced to being A<double>, and for the second overload T2 resolves to double. If there multiple overloads, the Standard says:
14.7.1 Implicit instantiation [temp.inst] clause 9
If a function template or a member function template specialization is
used in a way that involves overload resolution, a declaration of the
specialization is implicitly instantiated (14.8.3).
So at the end of argument deduction when the set of candidate functions are being generated, (so before overload resolution) the template gets instantiated and you get an error because rt does not have a nested type. This is why the more specialized second template will not be selected: overload resolution does not take place. You might have expected that this substitution failure would not be an error. HOwever, the Standard says:
14.8.2 Template argument deduction [temp.deduct] clause 8
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed if written using the substituted arguments.
Only invalid types and expressions in the immediate context of the
function type and its template parameter types can result in a
deduction failure. [ Note: The evaluation of the substituted types and
expressions can result in side effects such as the instantiation of
class template specializations and/or function template
specializations, the generation of implicitly-defined functions, etc.
Such side effects are not in the “immediate context” and can result in
the program being ill-formed. — end note ]
In your original code, the typename a_rt<T,T2>::type return type is not an immediate context. Only during template instantiation does it get evaluated, and then the lack of the nested type in rt is an erorr.
In your updated code A<typename rt<T,T2>::type> return type is an immediate context and the Substitution Failure is Not An Erorr (SFINAE) applies: the non-deduced function template is simply removed from the overload resolution set and the remaining one is being called.
With your updated code, output will be:
> A<T2>& called
> T2& called
Your forward declaration uses class:
template<typename T> class A;
But your definition uses struct:
template <typename T>
struct A {
Other than that, I can't see any problems...