What's the use of second parameter of std::enable_if? - c++

I am confused about the second parameter of std::enable_if.
In using of a return type of int, we can make it using:
template <class T>
typename std::enable_if<mpi::is_builtin<T>::value, int>::type
foo() { return 1; }
But how can I use enable_if in paramter or template? In this case, what's the difference of too functions below:
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
T too(T t) { std::cout << "here" << std::endl; return t; }
int too(int t) { std::cout << "there" << std::endl; return t; }
Thanks.

It means that in case of
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
it becomes
template<class T ,
class = void >
if the condition std::is_integral<T>::value is true, hence the function is allowed for the type T and therefore participates in overload resolution.
If the condition is not met, it becomes illegal and the typename std::enable_if<...>::type invalidates the function for the type T. In your example, the first method allows all integral types (int, unsigned, long, ...) but no classes, etc.
The second, int-only version in your example would loose some information and convert values from unsigned to signed or narrow some values, which is why the first version can be really helpful in some cases.
Note that void is actually the default for the second parameter of std::enable_if, which is often sufficient to enable or disable templates, etc. as you don't really need a specific type. All you need to know/detect is, whether or not it is valid (void) or invalid, in which case there is no valid substitution for the ::type part.

what's the difference of too functions below:
One is a template that can be called for any CopyConstructible type, the enable_if only constrains it when the default template argument is used:
#include <iostream>
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
T too(T t) { std::cout << "here" << std::endl; return t; }
int too(int t) { std::cout << "there" << std::endl; return t; }
int main()
{
too<double, void>(1.0);
}

Related

C++ Concepts check if variadic types equal a given type(s)

Using C++ concepts, I'd like to be able to define concepts that force all the types in a parameter pack to either be a given type, or in a list of given types. The 4 concepts I've defined are:
type_is: check if type T matches type U
type_in: check if type T is one of the types in U...
all_types_are: check if all types in parameter pack T... match type U
all_types_in: check if all types in parameter pack T... are any of the types in U... (can be different types in U...)
The code, with a method for each concept to test agsinst:
#include <concepts>
#include <iostream>
#include <type_traits>
class A{public: int var = 100;};
class B{public: int var = 200;};
class C{public: int var = 300;};
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = requires
{
requires std::same_as<std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
};
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
};
template <typename ...TypesToCheck, typename TypeToCheckAgainst>
concept all_types_are = requires
{
requires (std::same_as<std::remove_cvref_t<TypeToCheckAgainst>, TypesToCheck> && ...);
};
template <typename ...TypesToCheck, typename ...TypesToCheckAgainst>
concept all_types_in = requires
{
requires (std::same_as<std::remove_cvref_t<TypesToCheck>, TypesToCheckAgainst> || ...);
};
auto method1(type_is<A> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method2(type_in<A, B> auto&& object)
{
std::cout << object.var;
std::cout << std::endl;
}
auto method3(all_types_are<A> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(all_types_in<A, B> auto&&... objects)
{
(std::cout << ... << objects.var);
std::cout << std::endl;
}
int main()
{
A a;
B b;
C c;
method1(a);
method2(b);
method3(a, a, a);
method4(a, b, b);
return 0;
}
The first 2 concepts that check for a single type T work fine, but for the last 2 concepts, if I call methods constrained with those concepts, with arguments that should conform to the concepts, they still fail with:
main.cpp(75): error C2672: 'method3': no matching overloaded function found
main.cpp(75): error C7602: 'method3': the associated constraints are not satisfied
main.cpp(53): note: see declaration of 'method3'
main.cpp(76): error C2672: 'method4': no matching overloaded function found
main.cpp(76): error C7602: 'method4': the associated constraints are not satisfied
main.cpp(60): note: see declaration of 'method4'
I'm not sure how to correct my last two concepts so that they behave as desired, as I don't understand how the constraints aren't satisfied. What do I need to change in order to make the concepts work?
First, there is no need to use nested requires, just simple do
template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = std::same_as<
std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = (std::same_as<
std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
Second, you don't need to define all_types_are additionally, just reuse type_is/type_in for the variadic template version
auto method3(type_is<A> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}
auto method4(type_in<A, B> auto&&... objects) {
(std::cout << ... << objects.var);
std::cout << std::endl;
}
Demo
Your interpretation of the syntax in
auto method3(all_types_are<A> auto&&... objects)
is wrong. Using a type constraint in a template parameter pack doesn't result in the constraint being checked once for the whole pack expansion.
Instead the constraint will be checked for each element of the pack individually. So all you need is
auto method3(type_is<A> auto&&... objects)
to achieve what you intent all_types_are to do.
It is not possible to use a type constraint to constraint multiple types at once. If you need this (which is not the case here) you will need to use a requires clause instead, e.g.:
auto method4(auto&&... objects)
requires all_types_in<decltype(objects)..., A, B>
{ /*...*/ }
However, this will fail here as well, because there is no way to decide which of the template arguments in all_types_in<decltype(objects)..., A, B> are supposed to be part of TypesToCheck and which are supposed to be part of TypesToCheckAgainst.

Default template is matching despite static_assert

I am trying to create a templated function is compile-time enforced to use only specializations. I referenced Force a compile time error in a template specialization which suggests to use a static_assert on something inherited from std::false_type.
#include <iostream>
using namespace std;
template<typename T>
struct always_false : std::false_type {};
//Case: Default
template<typename T>
void foo(T val) {
static_assert(always_false<T>::value, "");
}
//Case: bool
template<>
void foo<bool>(bool val) {
cout << "Is explicitly a bool! " << val << endl;
}
//Case: int
template<typename T, typename std::enable_if<!std::is_same<T,bool>::value && std::is_convertible<T,int>::value,int>::type=0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
int main() {
foo(true); //(Good) Works correctly
foo((int)5); //(Bad) Error: call of overload foo(int) is ambiguous
foo((unsigned int)10); //(Bad) Error: call of overload foo(unsigned int) is ambiguous
foo((void*)nullptr); //(Good) Error: static assertion failed
return 0;
}
When I pass in an int or unsigned int, the compiler complains that the call is ambiguous suggesting that it can use either Case: Default or Case: int.
This is confusing as the Case: Default has the always_false static_assert() and I would expect the compiler to disallow it.
My last example passing in a void* successfully triggers the static_assert() and causes a compile-time error.
I am new to programming using SFINAE template metaprogramming, so I suspect I am doing something wrong in the Case: int specialization
Two questions:
Why is foo(int) in this code ambiguous?
Is there a better way to use
templates to get this desired behavior (explicit bool specialization + implicit integers specialization)?
Why is foo(int) in this code ambiguous?
Because the version with static_assert() give error if selected but still exist; so the compiler doesn't know if choose the generic version or the integer enabled version.
Is there a better way to use templates to get this desired behavior (explicit bool specialization + implicit int specialization)?
A possible way is to avoid the generic version and SFINAE enable the version you need
The following is a full working example
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_same<T, bool>::value>::type foo(T val)
{ std::cout << "bool case " << val << std::endl; }
template <typename T>
typename std::enable_if< ! std::is_same<T, bool>::value
&& std::is_convertible<T, int>::value>::type foo(T val)
{ std::cout << "integer case " << (int)val << std::endl; }
int main()
{
foo(true); // bool case
foo(1); // integer case
foo(2U); // integer case
foo(3L); // integer case
foo(4UL); // integer case
foo(5LL); // integer case
foo(6ULL); // integer case
// foo((void*)nullptr); // compilation error
}
-- EDIT --
The OP
Sorry, I am still confused. Could you elaborate? I thought that due to SFINAE, that if an error occurred in substitution, it would use the other template.
Exactly.
The problem is when there isn't an error in substitution and the compiler have to choose between two different version of the same template.
I mean: in your example, when you call foo(5), there isn't error in substitution of
typename std::enable_if<!std::is_same<T,bool>::value
&& std::is_convertible<T,int>::value,int>::type=0>
So the compiler have to choose between the two template functions
template<typename T>
void foo(T val) {
static_assert(always_false<T>::value, "");
}
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
that differ only for a template value with a default value, so are (from the compiler point of view) indistinguishable.
And observe that
template<>
void foo<bool>(bool val) {
cout << "Is explicitly a bool! " << val << endl;
}
is a (full) template specialization but
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
isn't a template specialization (no partial template specialization of a function is admitted in C++11/14/17; you can partial specialize only structs/classes); is a generic template.
You could use SFINAE as suggested by #max66, but a simple way for your use case would be to have a bool overload and a templated version
void foo(bool);
template <class T>
void foo(T);
You can enforce that T is convertible to int (static_assert) but in most cases it is not necessary because the body of foo will probably be ill-formed in such case, thus leading to a compile-time error.
template <class T>
void foo(T) {
static_assert(std::is_convertible<T, int>::value, "");
}
With your examples:
foo(true); // foo(bool) is chosen because it is the best match
foo((int)5); // foo<int>(int) is chosen, the assertion passes
foo((unsigned int)10); // foo<unsigned int>(unsigned int) is chosen, assertion ok
foo((void*)nullptr); // foo<void*>(void*) is chosen, the assertion fails

enable_if to Add a function parameter that has a default argument?

I can't understand the second scenario presented here. It says:
•Scenario 2: Adding a function parameter that has a default argument:
template <your_stuff> your_return_type_if_present
yourfunction(args, enable_if_t<your condition, FOO> = BAR) {
// ...
}
Scenario 2 leaves the parameter unnamed. You could say ::type Dummy = BAR, but the name Dummy is irrelevant, and giving it a name is likely to trigger an unreferenced parameter warning. You have to choose a FOO function parameter type and BAR default argument. You could say int and 0, but then users of your code could accidentally pass to the function an extra integer that would be ignored. Instead, we recommend that you use void ** and either 0 or nullptr because almost nothing is convertible to void **:
template <your_stuff> your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type=nullptr) {
// ...
}
If scenario 2 leaves the parameter unnamed then in what can it be used?
Is there a way to make a code like this work with enable_if?
enum otype {oadd,omull};
template<otype o>
int add(int num1, std::enable_if<o == oadd, int>::type int= num2)
{
if (o == omull) return num1 * num1;
if (o == oadd ) return num1 + num2;
}
Microsoft's documentation there
there is as clear as mud. Use this instead.
Providing a function template with an unnamed default parameter of the form:
typename enable_if<your_condition, void **>::type = nullptr
(as the MS scribe suggests), is useful in case - and only in case - you wish
to write multiple overloads of the function template with different behaviours
that are controlled by one or more of the template arguments. Then, by
replacing your_condition with a condition expressing an appropriate
requirement on the template argument(s), you can enlist the SFINAE
principle to select the specific overload that you want to be instantiated for
given template arguments.
The SFINAE parameter - let's call it that - is
unused by the instantiated function; it exists solely to provoke SFINAE in function template
overload resolution. Hence it can be nameless, and hence it must be defaulted:
it must not force you to provide an additional, useless, argument when you
invoke the function template.
For example:
#include <type_traits>
#include <iostream>
template <typename T>
T foo(T && t,
typename std::enable_if<std::is_same<T,int>::value, void **>::type = nullptr)
{
std::cout << "Doubling " << t << " gives " << (t + t) << std::endl;
return t + t;
}
template <typename T>
T foo(T && t,
typename std::enable_if<!std::is_same<T,int>::value, void **>::type = nullptr)
{
std::cout << "Squaring " << t << " gives " << (t * t) << std::endl;
return t * t;
}
using namespace std;
int main()
{
cout << foo(2) << endl;
cout << foo(3.3) << endl;
return 0;
}
Output is:
Doubling 2 gives 4
4
Squaring 3.3 gives 10.89
10.89
In these two overloads of function template foo, the first one doubles it's
type T argument and the second one squares its argument, and a SFINAE
parameter is used to determine that the doubling overload will be instantiated
if T is int and that the squaring overload will be chosen otherwise.
When T is int, the condition:
!std::is_same<T,int>::value
that controls the SFINAE parameter of the squaring overload is false. Consequently
the type specifier:
typename std::enable_if<!std::is_same<T,int>::value, void **>::type = nullptr
fails to compile. That is a substitution failure in template resolution. Substituting
int for T in the squaring overload is not viable. So the squaring overload is
eliminated from the running, and only the doubling overload is left to instantiate
the function call.
When T is (say) double and not int, then exactly the opposite happens
and only the squaring overload survives template resolution. Call foo(2)
and you get doubling. Call foo(3.3) and you get squaring.
MS's specimen SFINAE parameter here is needlessly lengthy.
template< bool B, class T = void >
struct enable_if;
as per C++11 Standard and later, defaults T to void. So the like of:
typename std::enable_if<some_condition, void **>::type = nullptr
can as well be abbreviated to:
typename std::enable_if<some_condition>::type * = nullptr
And as of C++14 the Standard has:
template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type
So the same SFINAE parameter can be further shortened to:
std::enable_if_t<some_condition> * = nullptr
Applying a SFINAE function template parameter to the case that you have gestured at in your
post, you would write the like of:
enum ops {
add,
multiply
};
template<ops Op>
int op(int const & lhs, int const & rhs,
std::enable_if_t<Op == add> * = nullptr)
{
return lhs + rhs;
}
template<ops Op>
int op(int const & lhs, int const & rhs,
std::enable_if_t<Op == multiply> * = nullptr)
{
return lhs * rhs;
}
...
auto i = op<add>(2,3);
auto j = op<multiply>(2,3);
...
// C++14
enable_if examples (if it helps):
For functions with non-void return type:
For single condition:
template <template T, typename std::enable_if<!std::is_same<T,std::string>::value>::type* = nullptr >
T func(T x){}
For multiple condition:
template <template T, typename std::enable_if<!std::is_same<T,std::string>::value &&!std::is_same<T,int>::value>::type* = nullptr >
T func(T x){}
For functions with void return type:
For single condition:
template <template T>
typename std::enable_if<!std::is_same<T,std::string>::value>::type
func(T x){}
For multiple condition:
template <template T>
typename std::enable_if<!std::is_same<T,std::string>::value &&!std::is_same<T,int>::value>::type
func(T x){}
Don't forget to include #include <type_traits>

C++: template to check if expression compiles

When writing template specialization with SFINAE you often come to the point where you need to write a whole new specialization because of one small not-existing member or function. I would like to pack this selection into a small statement like orElse<T a,T b>.
small example:
template<typename T> int get(T& v){
return orElse<v.get(),0>();
}
is this possible?
The intent of orElse<v.get(),0>() is clear enough, but if such a thing could exist,
it would have to be be one of:
Invocation Lineup
orElse(v,&V::get,0)
orElse<V,&V::get>(v,0)
orElse<V,&V::get,0>(v)
where v is of type V, and the function template thus instantiated
would be respectively:
Function Template Lineup
template<typename T>
int orElse(T & obj, int(T::pmf*)(), int deflt);
template<typename T, int(T::*)()>
int orElse(T & obj, int deflt);
template<typename T, int(T::*)(), int Default>
int orElse(T & obj);
As you appreciate, no such a thing can exist with the effect that you want.
For any anyone who doesn't get that,
the reason is simply this: None of the function invocations in the Invocation Lineup
will compile if there is no such member as V::get. There's no getting round
that, and the fact that the function invoked might be an instantiation of a
function template in the Function Template Lineup makes no difference whatever.
If V::get does not exist, then any code that mentions it will not compile.
However, you seem to have a practical goal that need not be approached
in just this hopeless way. It looks as if, for a given name foo and an given type R,
you want to be able to write just one function template:
template<typename T, typename ...Args>
R foo(T && obj, Args &&... args);
which will return the value of R(T::foo), called upon obj with arguments args...,
if such a member function exists, and otherwise return some default R.
If that's right, it can be achieved as per the following illustration:
#include <utility>
#include <type_traits>
namespace detail {
template<typename T>
T default_ctor()
{
return T();
}
// SFINAE `R(T::get)` exists
template<typename T, typename R, R(Default)(), typename ...Args>
auto get_or_default(
T && obj,
Args &&... args) ->
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
{
return obj.get(std::forward<Args>(args)...);
}
// SFINAE `R(T::get)` does not exist
template<typename T, typename R, R(Default)(), typename ...Args>
R get_or_default(...)
{
return Default();
}
} //namespace detail
// This is your universal `int get(T,Args...)`
template<typename T, typename ...Args>
int get(T && obj, Args &&... args)
{
return detail::get_or_default<T&,int,detail::default_ctor>
(obj,std::forward<Args>(args)...);
}
// C++14, trivially adaptable for C++11
which can be tried out with:
#include <iostream>
using namespace std;
struct A
{
A(){};
int get() {
return 1;
}
int get(int i) const {
return i + i;
}
};
struct B
{
double get() {
return 2.2;
}
double get(double d) {
return d * d;
}
};
struct C{};
int main()
{
A const aconst;
A a;
B b;
C c;
cout << get(aconst) << endl; // expect 0
cout << get(a) << endl; // expect 1
cout << get(b) << endl; // expect 0
cout << get(c) << endl; // expect 0
cout << get(a,1) << endl; // expect 2
cout << get(b,2,2) << endl; // expect 0
cout << get(c,3) << endl; // expect 0
cout << get(A(),2) << endl; // expect 4
cout << get(B(),2,2) << endl; // expect 0
cout << get(C(),3) << endl; // expect 0
return 0;
}
There is "compound SFINAE" in play in the complicated return type:
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
If T::get does not exist then decltype(obj.get(std::forward<Args>(args)...)
does not compile. But if it does compile, and the return-type of T::get is
something other than R, then the std::enable_if_t type specifier does not
compile. Only if the member function exists and has the desired return type R
can the R(T::get) exists case be instantiated. Otherwise the
catch-all R(T::get) does not exist case is chosen.
Notice that get(aconst) returns 0 and not 1. That's as it should be,
because the non-const overload A::get() cannot be called on a const A.
You can use the same pattern for any other R foo(V & v,Args...) and
existent or non-existent R(V::foo)(Args...).
If R is not default-constructible, or if you want the default R that
is returned when R(V::foo) does not exist to be something different from
R(), then define a function detail::fallback (or whatever) that returns the
desired default R and specify it instead of detail::default_ctor
How nice it would be it you could further template-paramaterize the pattern
to accomodate any possible member function of T with any possible return
type R. But the additional template parameter you would need for that would
be R(T::*)(typename...),and its instantiating value would have to be
&V::get (or whatever), and then the pattern would
force you into the fatal snare of mentioning the thing whose existence is in doubt.
Yes, this is more or less possible. It is known as a "member detector". See this wikibooks link for how to accomplish this with macros. The actual implementation will depend on whether you are using pre- or post-C++11 and which compiler you are using.

Type checking template class parameters

I am trying to achieve type checking of template class parameters by disallowing implicit type conversions such as string->bool thereby throwing compile error.
The specific scenario is a simple one as follows:
#include <iostream>
#include <string>
using namespace std;
template <class T>
class myPair {
T a, b;
public:
myPair(T first, T second ) {
a = first;
b = second;
}
void test();
};
typedef myPair<bool> boolParm;
template<class T>
void myPair<T>::test() {
if(a == true) {
cout << "a is true" << endl;
} else {
cout << "a is false" << endl;
}
if(b == true) {
cout << "b is true" << endl;
} else {
cout << "b is false" << endl;
}
}
int main() {
boolParm myObj(false, "false");
myObj.test();
return 0;
}
The output of the above scenario is undesirable since the user may inadvertently pass 2 different types: bool and string and receive the first one as false (correct since passed as bool) but the second one will be true (incorrect since implicit type conversion from string to bool).
I wish to restrict the user code in main() to throw compile errors and disallowing string/int parameters to pass in the constructor. It should only allow bool.
I tried by using an overloaded constructor myPair(bool first, string second) but it didn't match since I guess the implicit type conversion from string->bool happens before the constructor is called.
Is there any solution using template specializations in this scenario?
Any help is highly appreciated
Thanks
One workaround is to add a templated factory function to create the myPair.
template <typename T>
myPair<T> makeParam(T a, T b) {
return myPair<T>(a, b);
}
That will fail to compile with ambiguous template parameter T if the types don't match. You can extend this with template specializations explicitly forbidding certain types for T. Your main function will then look something like:
int main() {
boolParm myObj = makeParam(false, "false");
myObj.test();
return 0;
}
Alternatively change the constructor:
template <typename U, typename V>
myPair(U a, V b);
And specialize as necessary
An example of such specialization:
template <class T>
class myPair {
T a, b;
public:
template <typename U, typename V> // generic version
myPair(U first, V second)
{
// intentionally fail to compile
static_assert(false, "don't support generic types");
}
template <> // template specialization
myPair(T first, T second)
{
// explicitly require exactly type T
a = first;
b = second;
}
};
It is indeed weird behavior at first glance; but as far as I can say, you can't prohibit that - not for primitive types like bool, anyway.
The implicit conversion of parameters happen before you get a say on it, and it seems there is an implicit primitive type conversion from char const * to bool.
See also e.g. this other question: Why does a quoted string match bool method signature before a std::string?