std::enable_if to conditionally compile a member function
SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. There is no such substitution.
I thought of that too and tried to use
std::is_same< T, int >::value
and! std::is_same< T, int >::value
which gives the same result.
That's because when the class template is instantiated (which happens when you create an object of type Y<int>
among other cases), it instantiates all its member declarations (not necessarily their definitions/bodies!). Among them are also its member templates. Note that T
is known then, and !std::is_same< T, int >::value
yields false. So it will create a class Y<int>
which contains
class Y<int> {
public:
/* instantiated from
template < typename = typename std::enable_if<
std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/
template < typename = typename std::enable_if< true >::type >
int foo();
/* instantiated from
template < typename = typename std::enable_if<
! std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/
template < typename = typename std::enable_if< false >::type >
int foo();
};
The std::enable_if<false>::type
accesses a non-existing type, so that declaration is ill-formed. And thus your program is invalid.
You need to make the member templates' enable_if
depend on a parameter of the member template itself. Then the declarations are valid, because the whole type is still dependent. When you try to call one of them, argument deduction for their template arguments happen and SFINAE happens as expected. See this question and the corresponding answer on how to do that.
Selecting a member function using different enable_if conditions
enable_if
works because the substitution of a template argument resulted in an error, and so that substitution is dropped from the overload resolution set and only other viable overloads are considered by the compiler.
In your example, there is no substitution occurring when instantiating the member functions because the template argument T
is already known at that time. The simplest way to achieve what you're attempting is to create a dummy template argument that is defaulted to T
and use that to perform SFINAE.
template<typename T>
struct Point
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
std::cout << "T is not int." << std::endl;
}
};
Edit:
As HostileFork mentions in the comments, the original example leaves the possibility of the user explicitly specifying template arguments for the member functions and getting an incorrect result. The following should prevent explicit specializations of the member functions from compiling.
template<typename T>
struct Point
{
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is int." << std::endl;
}
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is not int." << std::endl;
}
};
usage of enable_if for non-templated member function [duplicate]
first of all, as mentioned by @jrok and the post you linked to:
you have to have a template function to use enable_if
. but it is not a good idea in your particular case, because there is no reason to do this operator->
templated. moreover, it wouldn't work! cuz there is no way to instantiate a particular operator->()! it doesn't have (and can't) any parameters and there is no syntax to specify it when you call it on some object! so, T
(or whateve dummy type) will/could not be deduced from this call.
so, as a workaround you may use compile-time conditional inheritance. i.e. smth like this:
template <typename T, bool IsClass>
struct smart_base
{
struct base {};
};
template <typename T>
struct smart_base<T, true>
{
struct base
{
T* operator->()
{
// do that ever you wanted to do
}
};
};
template <typename T>
struct smart : public smart_base<T, std::is_class<T>::value>::base
{
// depending on T here you have, or have no operator-> inherited
};
you have to understand, that having operator->
actually in your base class will require to move some function and/or data members to base-of-the-base class :) or you may use CRTP technique to access members of a derived class from the base one :)
enable_if to conditionally include member functions
std::enable_if
needs to depend on a parameter of the member template itself.
template <typename TagType>
class foo
{
public:
template <typename U = TagType>
typename std::enable_if<
std::is_base_of<std::bidirectional_iterator_tag,
U>::value,
foo>::type
operator --() {
return *this;
}
};
SFINAE will work as expected.
int main() {
foo<std::random_access_iterator_tag> f;
foo<std::forward_iterator_tag> f2;
--f; // fine
--f2;
}
main.cpp:24:3: error: no match for 'operator--' (operand type is 'foo<std::forward_iterator_tag>')
--f2;
C++ conditional template member function
If you can guarantee that all types appear only once, then the following should work:
template<typename... Ts>
class Executor {
using TupleOfCallback = std::tuple<std::function<void(Ts)>...>;
public:
Executor(const std::function<void(Ts)>&... func);
template<class E>
std::enable_if_t<(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity) {
std::get<std::function<void(E*)>>(m_Callbacks)(entity);
}
template<class E>
std::enable_if_t<!(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity)
{ }
public:
TupleOfCallback m_Callbacks;
};
The basic idea is to use fold-expression to detect whether E*
is included in Ts...
, thereby enabling the corresponding function.
Demo.
Using enable_if to prevent declaration? [duplicate]
You mustn't use this way because your object will be created in any case since the class doesn't have a condition to be created or not. And then here Enable_if<false, T> f0(int x) {};
, the condition is false
then there is no type. note that template class is instantiated on passing your parameter in the beginning and all that has done is replacing T
by void
.
one way you can use is as follows
#include <type_traits>
using namespace std;
template<bool B, typename T>
using Enable_if = typename std::enable_if<B,T>::type;
struct X {
template <class T>
Enable_if<true, T> f0(int x) {}
template <class T>
Enable_if<false, T> f0(int x) {}
};
int main(void)
{
X xx;
xx.f0<void>(4);
return 0;
}
Here we have two function templates, hence each one of them may be instantiated or not and this depends on the condition inside the class. If it's true, the function will be instantiated and vice versa. note that here only the needed function is instantiated.
conditional compilation of void argument member method using enable_if
Your first attempt with enable_if
does not work because SFINAE applies in overload resolution
of function (or member function) templates, where it will eliminate
a specialization of the function template from the overload set when that
specialization cannot compile.
The member hello
, in your first attempt, is not a member function template.
It has no template parameters. It is simply a member function of a class template.
Its return type is formulated by an enable_if
expression that
will provoke compilation failure if the class template parameter T
is not
instantiated as bool
. This doesn't make the member function itself into a template.
SFINAE has no application. Once you declare MyClass<float> myclass2
, the
specialization of MyClass<T>
and all its members is completely determined.
The member function hello
of that specialization must be instantiated,
and with T
= float
the attempt to do so must fail to compile.
In the second, successful attempt, hello
is a member function template (of
a class template). It has a template parameter, Q
, which by default is = T
.
So SFINAE applies and you can leverage it with enable_if
in the intended manner.
You may declare MyClass<float> myclass2
without error, because doing so
does not force any instantiation of the template member MyClass<float>::hello<Q>
Since you have written only one overload of hello
, there is only one specialization
of the member function template for any choice of Q
. When Q
= bool
, that
single specialization survives and myclass1.hello()
will
compile. When Q
!= bool
, SFINAE eliminates the that single specialization
and myclass2.hello()
does not compile.
To appeciate vividly how SFINAE in the second case is operating at instantation of
the member function template, consider that:
MyClass<float> myclass2;
myclass2.hello<bool>();
is fine; while on the other hand:
MyClass<bool> myclass1;
myclass1.hello<float>();
does not compile.
Here is documentation of SFINAE
SFINAE not working to conditionally compile member function template
Try with
template<typename T>
struct C {
template<typename Q = T,
typename std::enable_if<std::is_same<Q, int>::value, bool>::type = true>
int foo() { // .............................................^^^^^^^^^^^^^^^^^^^^
return 1;
}
template<typename Q = T,
typename std::enable_if<!std::is_same<Q, int>::value, bool>::type = true>
int foo() { // ..............................................^^^^^^^^^^^^^^^^^^^^
return 0;
}
};
the point is that in your code SFINAE will enable/disable default values for template type parameter; but default values do not participate in overload resolution, so, in your case, you have two functions
template<typename, typename = void>
int foo() {
return 1;
}
template<typename, typename>
int foo() {
return 0;
}
with the same signature; so the compiler can't choose between the two and will give an error.
The code I proposed is different because in case the test of std::enable_if
is false, you don't have the type (the element on the left of the =
), not the value. Something as
// ................VVVVVV what is = true ?
template<typename, = true>
int foo() {
return 1;
}
that is a true "substitution failure" that disable the method.
Related Topics
Finding Version of Microsoft C++ Compiler from Command-Line (For Makefiles)
How to Set Multiple Conditions for a Loop
How to Print Bytes as Hexadecimal
Difference Between Function Template and Template Function
How to Set, Clear, and Toggle a Single Bit
When Should Static_Cast, Dynamic_Cast, Const_Cast, and Reinterpret_Cast Be Used
Checking If All Elements of a Vector Are Equal in C++
While Writting Unittests for a Function, Should I Mock the Internal Function Calls Made
Carry Over Data Without Using for Loop
What Should Main() Return in C and C++
How to Iterate Over the Words of a String
Resolve Build Errors Due to Circular Dependency Amongst Classes
Remove Max Value from Simply-Connected List
How to Find Out If an Item Is Present in a Std::Vector
Avx2 What Is the Most Efficient Way to Pack Left Based on a Mask